Setup

library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.1 ──
✓ ggplot2 3.3.5     ✓ purrr   0.3.4
✓ tibble  3.1.4     ✓ dplyr   1.0.7
✓ tidyr   1.1.3     ✓ stringr 1.4.0
✓ readr   2.0.1     ✓ forcats 0.5.1
── Conflicts ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()
library(meta)
Loading 'meta' package (version 4.19-0).
Type 'help(meta)' for a brief overview.
library(cowplot)
source("data_import_functions.R")
source("figure_format_functions.R")
Loading required package: flextable

Attaching package: ‘flextable’

The following object is masked from ‘package:purrr’:

    compose
all_hla_expanded <- readRDS("all_hla_expanded.RDS")
isb_path <- "/labs/khatrilab/solomonb/covid/isb"

Running scHLA count

Prepare reference genotype files

# Assemble key linking sample-pool to simplified sample
hla_samples <- read_tsv("../../covid/isb/scHLAcount/all_fastq_files.txt", col_names = "file") %>% 
  filter(grepl("^INCOV", file)) %>% 
  filter(grepl("-BL", file)) %>% 
  separate(file, into = c("sample", NA), sep = "_", remove = F) %>% 
  drop_na()

# From all genotype field results, assemble highest resolution genotype for all sample:genotypers
select_last_allele <- function(x){
  x[!is.na(x)] %>%
    tail(1) %>% 
    str_replace_all("_", ":")}
hla_key <- all_hla_expanded %>% 
  rowwise() %>% 
  filter(grepl("^[ABC]", locus)) %>% 
  mutate(allele = select_last_allele(across(contains("field")))) %>% 
  select(sample, genotyper, locus, allele) %>% 
  unite(allele, locus, allele, sep = "*")

# Merge into key of list of genotypes for each sample-pool:genotyper pair
hla_merge <- hla_samples %>% 
  left_join(hla_key, by = "sample") %>% 
  drop_na() %>% 
  select(-sample) %>% 
  group_by(file, genotyper) %>% 
  nest()

# # Write keys to set of csvs for input to scHLAcount
# hla_merge %>% 
#   mutate(write = pmap(list(data, file, genotyper), function(d,f,g){
#     dir <- sprintf("../../covid/isb/scHLAcount/genotypes/%s",g)
#     if (!dir.exists(dir)){dir.create(dir, recursive = T)}
#     write_tsv(d, 
#               sprintf("%s/%s_hla.tsv",dir,f), 
#               col_names = F,
#               )}))

Run script

sbatch /covid/scripts/isb_scHLAcount_benchmark.sh

#!/bin/sh
#SBATCH --mail-type=END,FAIL
#SBATCH --mail-user=solomonb@stanford.edu
#SBATCH --time=13-23:05 # Runtime in D-HH:MM
#SBATCH --job-name=SCHLA
#SBATCH --nodes=1 # Ensure that all cores are reserved on one machine
#SBATCH --ntasks=6
#SBATCH --cpus-per-task=8
#SBATCH --mem-per-cpu=7G
#SBATCH --partition=khatrilab # Partition allocated for the lab
#SBATCH --error=./slurm_logs/%x.err
#SBATCH --output=./slurm_logs/%x.out

# SET GLOBAL VARIABLES
# General
export BASE_DIR=/labs/khatrilab/solomonb/covid/isb/scHLAcount
export LOG_DIR=$BASE_DIR/logs/$(date +'%y%m%d_%H%M%S')
# FASTQ/HISAT
export INDEX_DIR=/labs/khatrilab/solomonb/rnaseq_processing/hisat2/hisat_arcas/hisat_data/grch38
export BAM_DIR=$BASE_DIR/bam
# HLA references
export HLA_DIR=$BASE_DIR/hla_references
export HLANUC=/labs/khatrilab/solomonb/references/IMGTHLA/hla_nuc.fasta
export HLAGEN=/labs/khatrilab/solomonb/references/IMGTHLA/hla_gen.fasta
# SCHLA
export BARCODE_DIR=$BASE_DIR/barcodes
export GENOTYPE_DIR=$BASE_DIR/genotypes
export SCHLACOUNT_DIR=$BASE_DIR/output
export TEMP_DIR=$BASE_DIR/temp_fastq
# SLURM 
export N_CORES=$SLURM_CPUS_PER_TASK


# CREATE DIRECTORIES
if [ ! -d $LOG_DIR ]; then mkdir -p $LOG_DIR;fi
if [ ! -d $BAM_DIR ]; then mkdir -p $BAM_DIR;fi
if [ ! -d $SCHLACOUNT_DIR ]; then mkdir -p $SCHLACOUNT_DIR;fi
if [ ! -d $TEMP_DIR ]; then mkdir -p $TEMP_DIR;fi

# CREATE HLA REFERECE ##########################################################
HLA_REFERENCE(){
  source /labs/khatrilab/solomonb/miniconda3/etc/profile.d/conda.sh
  conda activate samtools
  
  printf "\n\
########################## CREATE REFERENCE ####################################\
  \n" >> $LOG_DIR/${1}/${1}_${2}.log
  printf "\n### [START___REFERENCE___$(date +'%D %X')]\n" >> $LOG_DIR/${1}/${1}_${2}.log
  
  [ ! -d $HLA_DIR/${2} ] && mkdir -p $HLA_DIR/${2}
  
  while read -r line; do grep -F -m 1 $line $HLANUC >> $HLA_DIR/${2}/${1}_tmpallele.txt; done < $GENOTYPE_DIR/${2}/${1}_hla.tsv
  
  samtools faidx  $HLANUC $(cut -f1 -d' ' $HLA_DIR/${2}/${1}_tmpallele.txt | tr '>' ' ' | tr '\n' ' ') > $HLA_DIR/${2}/${1}_cds.fasta 2>> $LOG_DIR/${1}/${1}_${2}.log
  samtools faidx  $HLAGEN $(cut -f1 -d' ' $HLA_DIR/${2}/${1}_tmpallele.txt | tr '>' ' ' | tr '\n' ' ') > $HLA_DIR/${2}/${1}_gen.fasta 2>> $LOG_DIR/${1}/${1}_${2}.log
  
  while read -r line; do IFS=' '; read -r f1 f2 <<<"$line"; sed -i"" "s/$f1/$f1 $f2/g" $HLA_DIR/${2}/${1}_cds.fasta; done < $HLA_DIR/${2}/${1}_tmpallele.txt 2>> $LOG_DIR/${1}/${1}_${2}.log
  while read -r line; do IFS=' '; read -r f1 f2 f3 <<<"$line"; sed -i"" "s/$f1/$f1 $f2/g" $HLA_DIR/${2}/${1}_gen.fasta; done < $HLA_DIR/${2}/${1}_tmpallele.txt 2>> $LOG_DIR/${1}/${1}_${2}.log
  
  rm $HLA_DIR/${2}/${1}_tmpallele.txt
  printf "### [COMPLETE___REFERENCE___$(date +'%D %X')]\n" >> $LOG_DIR/${1}/${1}_${2}.log
}
export -f HLA_REFERENCE


# DEFINE scHLA GENOTYPING PIPELINE #############################################
SCHLACOUNT(){
  source /labs/khatrilab/solomonb/miniconda3/etc/profile.d/conda.sh
  conda activate samtools
  
  printf "\n\
########################## RUN SCHLACOUNT ####################################\
  \n" >> $LOG_DIR/${1}/${1}_${2}.log
  printf "\n### [START___SCHLACOUNT___$(date +'%D %X')]\n" >> $LOG_DIR/${1}/${1}_${2}.log
  
  echo "### Starting  scHLAcount at $(date +'%D %X')" >> $LOG_DIR/${1}/${1}_${2}.log
  
  # if [ -d $SCHLACOUNT_DIR/${1}_results ]; then rm -r $SCHLACOUNT_DIR/${1}_results;fi
  [ ! -d SCHLACOUNT_DIR/${2} ] && mkdir -p $SCHLACOUNT_DIR/${2}

  sc_hla_count \
  --bam $BAM_DIR/${1}.bam \
  --cell-barcodes $BARCODE_DIR/${1}_barcode.tsv \
  --out-dir $SCHLACOUNT_DIR/${2}/${1}_results \
  --fasta-cds $HLA_DIR/${2}/${1}_cds.fasta \
  --fasta-genomic $HLA_DIR/${2}/${1}_gen.fasta\
  >> $LOG_DIR/${1}/${1}_${2}.log \
  2>> $LOG_DIR/${1}/${1}_${2}.log

  printf "### [COMPLETE___SCHLACOUNT___$(date +'%D %X')]\n" >> $LOG_DIR/${1}/${1}_${2}.log
}
export -f SCHLACOUNT


# DEFINE PIPELINE CONTROLLER FUNCTION ##########################################
PIPELINE(){
  echo "START: sample $1 at $(date +'%D %X')"
  for G in arcasHLA hlaminer invitro optitype phlat
  do
    [ ! -d $LOG_DIR/${1} ] && mkdir -p $LOG_DIR/${1}
    HLA_REFERENCE $1 $G
    SCHLACOUNT $1 $G
  done
  echo "COMPLETE: sample $1 at $(date +'%D %X')"
}
export -f PIPELINE

cat $BASE_DIR/BL_fastq_files.txt | parallel --delay 15 -j $SLURM_NTASKS --joblog $LOG_DIR/parallel.log PIPELINE {}

UMAP projections

Cell clusters

srt <- readRDS("/labs/khatrilab/hongzheng/webapps/isb/srt_isb260.meta.rds")
cells <- c("CD14 Monocyte", "CD4 T", "CD8 T", "NK", "B", "CD16 Monocyte", "cDC", "pDC")
# srt <- srt %>% filter(celltype %in% cells)
# ggplot theme to overlay cluster id #s on UMAP
gg_srt_relabel <- function(df, x_var, y_var, color_var, cell_fraction = 1){
  plt <- df %>% 
    ggplot(aes(x = !!sym(x_var), y = !!sym(y_var), color = !!sym(color_var))) +
    geom_point(size = 0.5, alpha = 0.5)+
    theme_bw() +
    scale_color_brewer(palette = "Dark2")+
    guides(color = guide_legend(override.aes = list(size = 2, alpha = 1)))
  
  g <- ggplot_build(plt)
  
  plt_ids <- g$data[[1]]
  group_levels <- levels(factor(g$plot$data[[g$plot$labels$colour]]))
  
  plt_key <- g$data[[1]] %>% 
    select(colour, group) %>% 
    distinct() %>% 
    mutate(label = map_chr(group, function(x) group_levels[x])) %>% 
    mutate(label = factor(label, level = group_levels)) %>%
    mutate(label = sprintf("%s) %s", 1:n(), label)) %>% 
    select(-group)
  
  plt_df <- plt_ids %>% 
    left_join(plt_key, by = "colour")
  
  plt_center <- plt_df %>% 
    group_by(label) %>% summarise(x = mean(x), y = mean(y)) %>%
    mutate(label = gsub(").*","",label))
  
  plt_repel <- plt_df %>% 
    ggplot(aes(x=x,y=y,color=label)) +
    geom_point(size = 0.5)+
    ggrepel::geom_text_repel(data=plt_center,
                             aes(label=label,  bg.color="white", bg.r=0.25),
                             color = "black",
                             fontface = "bold") +
    theme_bw() +
    guides(color = guide_legend(override.aes = list(size = 2) ) ) +
    labs(x=x_var, y = y_var, color = color_var) +
    theme(axis.text = element_blank(), axis.ticks = element_blank())
  return(plt_repel)
}

plt_srt <- srt %>% 
  filter(celltype %in% cells) %>% 
  filter(sampleID == "INCOV069-BL") %>% 
  gg_srt_relabel(x_var = "UMAP_1", y_var = "UMAP_2", color_var = "celltype", cell_fraction = 0.1) +
  scale_color_brewer(palette = "Dark2")+ 
  theme(axis.text = element_blank(), axis.ticks = element_blank()) +
  labs(x="UMAP 1", y = "UMAP 2", color = "Cell Cluster")
plt_srt

Allele frequency

samp <- "INCOV069-BL"
# samp <- "INCOV028-BL"
gene_select <- "A"
scHLAcount_dir <- sprintf("%s/scHLAcount", isb_path)

# Create tibble of all genotyper and sample combinations
allele_data <- expand_grid(
  genotyper = c("invitro", "arcasHLA", "optitype", "phlat", "hlaminer"),
  sample = read_lines(
    "/labs/khatrilab/solomonb/covid/isb/scHLAcount/BL_fastq_files.txt"
  )) %>% 
  filter(grepl(samp, sample)) %>% 
  # Import data based on sample and genotyper
  mutate(result_path = sprintf("%s/output/%s",scHLAcount_dir, genotyper),
         barcode_path = sprintf("%s/barcodes", scHLAcount_dir)) %>% 
  mutate(data = pmap(list(sample, result_path, barcode_path), function(s,r,b){
    df <- scHLA_data_processing(
      sample=s,
      result_dir=r,
      barcode_dir=b
    ) 
  })) %>% unnest(data)


allele_data_ratios <- allele_data %>% 
  filter(gene == gene_select) %>% 
  mutate(genotyper = reformat_hla_genotyper(genotyper)) %>% 
  mutate(allele_order = fct_recode(factor(allele_order), "Allele 1" = "1", "Allele 2" = "2"))
allele_data
  
allele_label <- allele_data_ratios %>% 
  select(genotyper, allele_order, allele) %>% 
  distinct()

plt <- srt %>% 
  left_join(allele_data_ratios %>% filter(gene == gene_select), by = "cell") %>% 
  filter(!is.na(allele)) %>% 
  ggplot(aes(x = UMAP_1, y = UMAP_2, color = allele_ratio)) +
  geom_point(size = 0.5, alpha = 0.5)+
  theme_bw() +
  facet_grid(allele_order~genotyper)+
  scale_color_gradient2(high = "red", mid = "grey80", low = "blue", midpoint = 0.5, na.value = "transparent") +
  labs(x="UMAP 1", y="UMAP 2", color = "Allele \nFrequency")+
  theme(axis.ticks = element_blank(), axis.text = element_blank())+
  coord_cartesian(ylim = c(-10,15))
plt_allele_freq <- plt+
  geom_label(data = allele_label, aes(x=-10,y=14, color = NULL, label = allele), size = 3, hjust = 0)
plt_allele_freq

Maximum allele

# samp <- "INCOV069-BL"
# samp <- "INCOV022-BL"
samp <- "INCOV028-BL"
# samp <- "INCOV083-BL"
scHLAcount_dir <- sprintf("%s/scHLAcount", isb_path)

# Create tibble of all genotyper and sample combinations
allele_data <- expand_grid(
  genotyper = c("invitro", "arcasHLA", "optitype", "phlat", "hlaminer"),
  sample = read_lines(
    "/labs/khatrilab/solomonb/covid/isb/scHLAcount/BL_fastq_files.txt"
  )) %>% 
  filter(grepl(samp, sample)) %>% 
  # Import data based on sample and genotyper
  mutate(result_path = sprintf("%s/output/%s",scHLAcount_dir, genotyper),
         barcode_path = sprintf("%s/barcodes", scHLAcount_dir)) %>% 
  mutate(data = pmap(list(sample, result_path, barcode_path), function(s,r,b){
    df <- scHLA_data_processing(
      sample=s,
      result_dir=r,
      barcode_dir=b
    ) 
  })) %>% unnest(data)

# Create tibble of all genotyper and sample combinations
allele_max_ratio <- allele_data  %>%
  select(genotyper, cell, gene, allele_order, allele_ratio) %>%
  # group_by(genotyper, cell, allele_order) %>%  mutate(test = length(allele_order))
  pivot_wider(names_from = "allele_order", values_from = "allele_ratio", names_prefix = "allele_") %>%
  rowwise() %>%
  mutate(max_ratio = max(across(contains("allele_")), na.rm = T)) %>%
  select(genotyper, cell, gene, max_ratio) %>%
  filter(gene == gene_select) 

allele_label <- allele_data %>% select(sample, genotyper, gene, allele) %>% 
  filter(gene == gene_select) %>% 
  separate(sample, into = c("sample", NULL), sep = "_", extra = "drop") %>% 
  distinct() %>% 
  group_by(sample, genotyper, gene) %>% nest() %>% unnest_wider(data) %>% 
  mutate(genotyper = reformat_hla_genotyper(genotyper)) %>% 
  mutate(allele = map_chr(allele, paste, collapse = "\n"))

plt_max_allele <- srt %>% 
  left_join(allele_max_ratio %>% filter(gene == "A"), by = "cell") %>% 
  # mutate(genotyper = fct_relevel(genotyper, "invitro")) %>% 
  mutate(genotyper = reformat_hla_genotyper(genotyper)) %>% 
  filter(!is.na(genotyper)) %>%
  ggplot(aes(x = UMAP_1, y = UMAP_2, color = max_ratio)) +
    geom_point(size = 0.5, alpha = 0.5)+
    geom_label(data = allele_label, aes(color = NULL, label = allele, x = -Inf, y = -Inf),
            hjust = -0.1, vjust = -0.1, size = 3, hjust = 0) +
    theme_bw() +
    facet_grid(.~genotyper)+
    scale_color_gradient(high = "red", low = "blue", na.value = "transparent") +
    labs(x="UMAP 1", y="UMAP 2", color = "Maximum Allele \nFrequency")+
    theme(axis.ticks = element_blank(), axis.text = element_blank())

plt_max_allele 

plt_max_allele$genotyper %>% reformat_hla_genotyper() %>% levels()
[1] "Ground truth" "arcasHLA"     "HLAminer"     "PHLAT"        "OptiType"    

Meta-analysis approach

Rationale

  • Goal: determine a summary statistic for allele ratio across all cells
  • Problem:
    • Each cell in scRNA experiment has its own ratio of HLA allele 1 reads : HLA allele 2 reads
    • A wide range of total read counts may go into each cell’s ratio
    • Small difference in read counts for cells with low total reads can greatly alter the allele ratio compared to cells with high total read counts
      • I.e. Cells with low read counts are more likely to have imprecise allele ratios
    • Simply averaging ratios would equally weight high confidence, high read count ratios with low confidence, low read ratios
  • Approach
    • Meta-analysis methods specifically address type of problem, typically by weighing an effect by the inverse of its variance
    • In the setting of ratios, basic approach would be to combine log-odds ratio of alleles, weighted by inverse variance
    • Would use a random effects model because do not expect each cell in a group would have the same exact ratio due to various stochastic transcriptional effects

Calculating summary effect sizes

  • Analysis can take some time, so run in slurm
  • Contained in a separate script ./slurm/sc_meta.R:

Contents of sc_meta.R

library(tidyverse)
library(meta)

project_dir <- "/labs/khatrilab/solomonb/hla_project/hla_benchmark"
source(sprintf("%s/data_import_functions.R", project_dir))
isb_path <- "/labs/khatrilab/solomonb/covid/isb"
scHLAcount_dir <- sprintf("%s/scHLAcount", isb_path)

srt <- readRDS("/labs/khatrilab/hongzheng/webapps/isb/srt_isb260.meta.rds")
cells <- c("CD14 Monocyte", "CD4 T", "CD8 T", "NK", "B", "CD16 Monocyte", "cDC", "pDC")

# Function to perform meta-analysis on dataframe where
# each row is a cell and columns:
# `observed` (reads of dominant allele)
# `gene_sum_typed` (total cell reads)
# `expected` (reads of one allele expected if 50:50 allele_1 : allele_2)
sc_meta <- function(df){
  l <- nrow(df)
  m <- metabin(event.e = observed, n.e = gene_sum_typed, event.c = expected, n.c = gene_sum_typed,
               data = df,
               method = "Inverse",
               incr = 0.1,
               sm = "OR")
  data.frame(summary(m)$random) %>% 
    mutate(n_cells = l) %>% 
    select(TE, seTE, lower, upper, n_cells) 
}

# Import data based on sample and genotyper
cell_stats <- expand_grid(
  genotyper = c("invitro", "arcasHLA", "optitype", "phlat", "hlaminer"),
  sample = read_lines(
    "/labs/khatrilab/solomonb/covid/isb/scHLAcount/BL_fastq_files.txt"
  )) %>%
  # head(5) %>% # Specify limit to number of meta-analyses
  mutate(data = map2(sample, genotyper, function(s,g){
    result_path = sprintf("%s/output/%s", scHLAcount_dir, g)
    barcode_path = sprintf("%s/barcodes", scHLAcount_dir)
    scHLA_data_processing(sample = s,
                          result_dir = result_path,
                          barcode_dir = barcode_path)
  })) %>% unnest(data) %>% 
  filter(!is.na(cell)) %>% 
  mutate(sample = gsub("_[A-Z][0-9]$","",sample)) %>% # Consolidate samples
  left_join(srt %>% select(celltype, cell), by = "cell") %>% # Add celltypes
  filter(celltype %in% cells) # Keep only standard cell types


meta_df <- cell_stats %>% 
  # Keep only most expressed allele (or random if 50:50)
  group_by(sample, genotyper, gene, cell) %>% 
  slice_max(order_by = allele_ratio, n = 1, with_ties = F) %>% 
  ungroup() %>% 
  # Fill out contingency table
  mutate(complement = gene_sum_typed - count, 
         expected = 0.5*gene_sum_typed) %>% 
  rename(observed = count) %>% 
  select(sample, genotyper, celltype, gene, observed, expected, gene_sum_typed) %>% 
  group_by(sample, genotyper, celltype, gene) %>%
  # Nest and run meta-analysis
  nest() %>%
  ungroup() %>% 
  mutate(data = map(data,function(x) {sc_meta(x)})) %>%
  unnest(data)

write_csv(meta_df, sprintf("%s/slurm/meta_analysis_results.csv", project_dir))

Single sample analysis

meta_df <- read_csv("./slurm/meta_analysis_results.csv") 
Rows: 7299 Columns: 9
── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (4): sample, genotyper, celltype, gene
dbl (5): TE, seTE, lower, upper, n_cells

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
meta_df
# Single celltype and gene
# samp <- "INCOV069-BL"
# samp <- "INCOV022-BL"
samp <- "INCOV028-BL"
# samp <- "INCOV083-BL"
plt_meta <- meta_df %>% 
  filter(sample == samp) %>% 
  filter(celltype == "NK", gene == "A") %>% 
  mutate(genotyper = reformat_hla_genotyper(genotyper)) %>% 
  ggplot(aes(x = genotyper, y = TE, ymin = lower, ymax = upper))+
  geom_point()+
  geom_errorbar()+
  theme_bw()+
  scale_y_continuous(limits = c(0,NA))+
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
  labs(y = "Log-Odds ratio of dominant allele", x= NULL)
plt_meta

samp <- "INCOV069-BL"
samples <- unique(meta_df$sample)
for (i in 1:length(samples)){
plt_meta <- meta_df %>% 
  filter(sample == samples[i]) %>% 
  filter(celltype == "CD14 Monocyte", gene == "A") %>% 
  mutate(genotyper = reformat_hla_genotyper(genotyper)) %>% 
  ggplot(aes(x = genotyper, y = TE, ymin = lower, ymax = upper))+
  geom_point()+
  geom_errorbar()+
  theme_bw()+
  scale_y_continuous(limits = c(0,NA))+
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
  labs(y = "Log-Odds ratio of dominant allele", x= NULL) +
  ggtitle(samples[i])
  print(plt_meta)
}

# All celltypes and genes
meta_df %>% 
  mutate(
    celltype = factor(celltype, levels = c(
    "cDC", "CD14 Monocyte", "B", "pDC", "CD16 Monocyte", "CD8 T", "CD4 T", "NK"))) %>% 
  mutate(genotyper = reformat_hla_genotyper(genotyper)) %>% 
  mutate(gene = reformat_hla_loci(gene)) %>% 
  ggplot(aes(x = celltype, y = TE, ymin = lower, ymax = upper, fill = celltype))+
    geom_bar(stat = "identity", position = "dodge", color = "black", size = 0.25)+
    geom_errorbar(position = position_dodge(0.9), width =0.5)+
    theme_bw()+
    facet_grid(gene~genotyper, scales = "free_y")+
    theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
    labs(y = "Log-Odds ratio of dominant allele", x= NULL, fill = "Cell type") +
    scale_fill_brewer(palette = "Dark2")


meta_df %>% 
  mutate(
    celltype = factor(celltype, levels = c(
    "cDC", "CD14 Monocyte", "B", "pDC", "CD16 Monocyte", "CD8 T", "CD4 T", "NK"))) %>% 
  mutate(genotyper = reformat_hla_genotyper(genotyper)) %>% 
  mutate(gene = reformat_hla_loci(gene)) %>% 
  ggplot(aes(x = genotyper, y = TE, ymin = lower, ymax = upper, fill = celltype))+
    geom_bar(stat = "identity", position = "dodge", color = "black", size = 0.25)+
    geom_errorbar(position = position_dodge(0.9), width =0.5)+
    theme_bw()+
    facet_grid(gene~celltype, scales = "free_y")+
    theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
    labs(y = "Log-Odds ratio of dominant allele", x= NULL, fill = "Cell type") +
    scale_fill_brewer(palette = "Dark2")

Multi-sample analysis

meta_df <- read_csv("./slurm/meta_analysis_results.csv") %>% 
  mutate(genotyper = reformat_hla_genotyper(genotyper),
         celltype = factor(celltype, levels = c(
          "cDC", "CD14 Monocyte", "B", "pDC", "CD16 Monocyte", "CD8 T", "CD4 T", "NK")))
Error in read_csv("./slurm/meta_analysis_results.csv") %>% mutate(genotyper = reformat_hla_genotyper(genotyper),  : 
  could not find function "%>%"
meta_df %>% 
  ggplot(aes(x=celltype,y=TE, fill = celltype))+
  geom_violin()+
  geom_jitter(alpha = 0.2, size = 0.2)+
  stat_summary(fun=mean, stat= "point")+
  stat_summary(fun.data = mean_se, geom="errorbar")+
  facet_grid(gene~genotyper)+
  theme_bw()+
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
  labs(y = "Log-Odds ratio of dominant allele", x= NULL, fill = "Cell type") +
  scale_fill_brewer(palette = "Dark2")


meta_df %>% 
  ggplot(aes(x=genotyper,y=TE, fill = celltype))+
  geom_violin()+
  geom_jitter(alpha = 0.2, size = 0.2)+
  stat_summary(fun=mean, stat= "point")+
  stat_summary(fun.data = mean_se, geom="errorbar")+
  facet_grid(gene~celltype)+
  theme_bw()+
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
  labs(y = "Log-Odds ratio of dominant allele", x= NULL, fill = "Cell type") +
  scale_fill_brewer(palette = "Dark2")

Correlation analysis

accuracy_df <- readRDS("isb_accuracy_drb345_filtered.RDS") %>% 
  mutate(genotyper = reformat_hla_genotyper(genotyper)) %>% 
  select(sample, gene=locus, genotyper, accuracy) %>% 
  distinct()
for (i in c(0,1)){
  suppressWarnings({
  plt <- meta_df %>% 
    ungroup() %>% 
    filter(gene == "A", celltype == "cDC") %>% 
    left_join(accuracy_df, by = c("sample", "gene", "genotyper")) %>% 
    filter(accuracy == i | genotyper == "Ground truth") %>% 
    # drop_na(accuracy) %>% 
    select(sample, genotyper, TE) %>% 
    pivot_wider(names_from = "genotyper", values_from = "TE") %>% 
    select(-sample) %>% 
    GGally::ggpairs(progress = FALSE) +
    theme_bw() +
    ggtitle(sprintf("Correlation of allele ratios where accuracy = %s", i))
  print(plt)
  })
}

for (i in c(0,1)){
  suppressWarnings({
  plt <- corr_df %>% 
    ungroup() %>% 
    # filter(gene == "A", celltype == "cDC") %>% 
    left_join(accuracy_df, by = c("sample", "gene", "genotyper")) %>% 
    filter(accuracy == i) %>% 
    ggplot(aes(x=`Ground truth`, y=TE))+
    geom_point(size = 0.5)+
    facet_grid(gene ~ genotyper) +
    theme_bw() +
    ggpubr::stat_cor(aes(label = ..rr.label..), label.x.npc = "left", label.y.npc = "top", geom = "label") +
      ggtitle(sprintf("Correlation of allele ratios where accuracy = %s", i)) +
    labs(y = "Predicted genotype HLA allele log-odds ratio", x = "Ground truth genotype HLA allele log-odds ratio")
  assign(sprintf("plt_meta_corr_%s", i), plt)
  print(plt)
  })
}
plt_meta_corr_abbrev <- corr_df %>% 
  left_join(accuracy_df, by = c("sample", "gene", "genotyper")) %>% 
  filter(gene == "A", accuracy %in% c(0,0.5,1)) %>%
  ggplot(aes(x=`Ground truth`, y=TE))+
    geom_point(size = 0.5)+
    facet_grid(accuracy ~ genotyper, labeller = labeller(accuracy = function(x) sprintf("Accuracy: %s", x))) +
    theme_bw() +
    ggpubr::stat_cor(aes(label = ..rr.label..), label.x.npc = "left", label.y.npc = "top", geom = "label", method = "pearson") +
    labs(y = "Log-odds ratio using predicted genotype", x = "Log-odds ratio using ground truth genotyper")
plt_meta_corr_abbrev
Warning: Removed 178 rows containing non-finite values (stat_cor).
Warning: Removed 178 rows containing missing values (geom_point).

By severity

meta_df %>% 
  filter(genotyper == "Ground truth") %>% 
  left_join(
    srt %>% 
      select(sample = sampleID, severity) %>% 
      distinct(),
    by = "sample"
  ) %>% 
  ggplot(aes(x=severity,y=TE, fill = celltype))+
    geom_violin()+
    geom_jitter(alpha = 0.2, size = 0.2)+
    stat_summary(fun=mean, stat= "point")+
    stat_summary(fun.data = mean_se, geom="errorbar")+
    facet_grid(gene~celltype)+
    theme_bw()+
    theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
    labs(y = "Log-Odds ratio of dominant allele", x= NULL, fill = "Cell type") +
    scale_fill_brewer(palette = "Dark2")

meta_df %>% 
  filter(genotyper == "Ground truth") %>% 
  left_join(
    srt %>% 
      select(sample = sampleID, severity) %>% 
      distinct(),
    by = "sample"
  ) %>% 
  mutate(severity = as.numeric(severity)) %>% 
  ggplot(aes(x = severity, y = TE)) +
  geom_point() + 
  stat_smooth(method = "lm")+
  facet_wrap(~celltype)+
  theme_bw() 
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 24 rows containing non-finite values (stat_smooth).
Warning: Removed 24 rows containing missing values (geom_point).

Plots for figures

Assemble plot

# plt_allele_freq_lgd <- cowplot::get_legend(plt_allele_freq)
# plt_max_allele_lgd <- cowplot::get_legend(plt_max_allele)
col_1 <- plot_grid(
  plt_allele_freq,
  plt_max_allele,
  ncol = 1,
  rel_heights = c(5,3),
  align = "v", axis = "lr",
  labels = LETTERS[1:2]
)

col_2 <- plot_grid(
  plt_srt,
  plt_meta,
  ncol = 1,
  align = "v", axis = "lr",
  labels = LETTERS[3:4],
  hjust = 0.5
)
row_1 <- plot_grid(
  col_1,
  col_2,
  nrow = 1,
  rel_widths = c(6,3)
)
row_2 <- plot_grid(
  plt_meta_corr_0 +ggtitle(NULL),
  plt_meta_corr_1 +ggtitle(NULL),
  labels = LETTERS[5:6],
  ncol = 2
)
Warning: Removed 334 rows containing non-finite values (stat_cor).
Warning: Removed 334 rows containing missing values (geom_point).
Warning: Removed 14 rows containing non-finite values (stat_cor).
Warning: Removed 14 rows containing missing values (geom_point).
plot_grid(
  row_1,
  row_2,
  rel_heights = c(3,2),
  ncol = 1
)

col_1 <- plot_grid(
  plt_srt,
  plt_meta,
  ncol = 1,
  align = "v", axis = "lr",
  labels = LETTERS[c(1,3)],
  label_x = -0.05
)

col_2 <- plot_grid(
  plt_max_allele,
  plt_meta_corr_abbrev,
  ncol = 1,
  rel_heights = c(3,5),
  align = "v", axis = "lr",
  labels = LETTERS[c(2,4)],
  label_x = -0.05
)
Warning: Removed 178 rows containing non-finite values (stat_cor).
Warning: Removed 178 rows containing missing values (geom_point).
plt_fig_main <- plot_grid(
  NULL,
  col_1,
  col_2,
  nrow = 1,
  rel_widths = c(0.25,3,6)
)

plt_fig_main

save_plot("figures_pdf/v2/7_scHLA.pdf", plt_fig_main, base_height = 7, base_width = 14)

Save all figure objects

listN <- function(...) {
  anonList <- list(...)
  names(anonList) <- as.character(substitute(list(...)))[-1]
  anonList
}

saveRDS(
  listN(plt_srt,
        plt_meta,
        plt_max_allele,
        plt_meta_corr_abbrev
        ),
  "figures_object/7_scHLA_objects.RDS"
)

Old work

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZGVwdGg6IDMKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBoaWRlCi0tLQoKIyBTZXR1cApgYGB7ciwgbWVzc2FnZSA9IEZ9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KG1ldGEpCmxpYnJhcnkoY293cGxvdCkKc291cmNlKCJkYXRhX2ltcG9ydF9mdW5jdGlvbnMuUiIpCnNvdXJjZSgiZmlndXJlX2Zvcm1hdF9mdW5jdGlvbnMuUiIpCmFsbF9obGFfZXhwYW5kZWQgPC0gcmVhZFJEUygiYWxsX2hsYV9leHBhbmRlZC5SRFMiKQppc2JfcGF0aCA8LSAiL2xhYnMva2hhdHJpbGFiL3NvbG9tb25iL2NvdmlkL2lzYiIKYGBgCgoKIyBSdW5uaW5nIHNjSExBIGNvdW50CgojIyMgUHJlcGFyZSByZWZlcmVuY2UgZ2Vub3R5cGUgZmlsZXMKCmBgYHtyLCBldmFsPUZ9CiMgQXNzZW1ibGUga2V5IGxpbmtpbmcgc2FtcGxlLXBvb2wgdG8gc2ltcGxpZmllZCBzYW1wbGUKaGxhX3NhbXBsZXMgPC0gcmVhZF90c3YoIi4uLy4uL2NvdmlkL2lzYi9zY0hMQWNvdW50L2FsbF9mYXN0cV9maWxlcy50eHQiLCBjb2xfbmFtZXMgPSAiZmlsZSIpICU+JSAKICBmaWx0ZXIoZ3JlcGwoIl5JTkNPViIsIGZpbGUpKSAlPiUgCiAgZmlsdGVyKGdyZXBsKCItQkwiLCBmaWxlKSkgJT4lIAogIHNlcGFyYXRlKGZpbGUsIGludG8gPSBjKCJzYW1wbGUiLCBOQSksIHNlcCA9ICJfIiwgcmVtb3ZlID0gRikgJT4lIAogIGRyb3BfbmEoKQoKIyBGcm9tIGFsbCBnZW5vdHlwZSBmaWVsZCByZXN1bHRzLCBhc3NlbWJsZSBoaWdoZXN0IHJlc29sdXRpb24gZ2Vub3R5cGUgZm9yIGFsbCBzYW1wbGU6Z2Vub3R5cGVycwpzZWxlY3RfbGFzdF9hbGxlbGUgPC0gZnVuY3Rpb24oeCl7CiAgeFshaXMubmEoeCldICU+JQogICAgdGFpbCgxKSAlPiUgCiAgICBzdHJfcmVwbGFjZV9hbGwoIl8iLCAiOiIpfQpobGFfa2V5IDwtIGFsbF9obGFfZXhwYW5kZWQgJT4lIAogIHJvd3dpc2UoKSAlPiUgCiAgZmlsdGVyKGdyZXBsKCJeW0FCQ10iLCBsb2N1cykpICU+JSAKICBtdXRhdGUoYWxsZWxlID0gc2VsZWN0X2xhc3RfYWxsZWxlKGFjcm9zcyhjb250YWlucygiZmllbGQiKSkpKSAlPiUgCiAgc2VsZWN0KHNhbXBsZSwgZ2Vub3R5cGVyLCBsb2N1cywgYWxsZWxlKSAlPiUgCiAgdW5pdGUoYWxsZWxlLCBsb2N1cywgYWxsZWxlLCBzZXAgPSAiKiIpCgojIE1lcmdlIGludG8ga2V5IG9mIGxpc3Qgb2YgZ2Vub3R5cGVzIGZvciBlYWNoIHNhbXBsZS1wb29sOmdlbm90eXBlciBwYWlyCmhsYV9tZXJnZSA8LSBobGFfc2FtcGxlcyAlPiUgCiAgbGVmdF9qb2luKGhsYV9rZXksIGJ5ID0gInNhbXBsZSIpICU+JSAKICBkcm9wX25hKCkgJT4lIAogIHNlbGVjdCgtc2FtcGxlKSAlPiUgCiAgZ3JvdXBfYnkoZmlsZSwgZ2Vub3R5cGVyKSAlPiUgCiAgbmVzdCgpCgojICMgV3JpdGUga2V5cyB0byBzZXQgb2YgY3N2cyBmb3IgaW5wdXQgdG8gc2NITEFjb3VudAojIGhsYV9tZXJnZSAlPiUgCiMgICBtdXRhdGUod3JpdGUgPSBwbWFwKGxpc3QoZGF0YSwgZmlsZSwgZ2Vub3R5cGVyKSwgZnVuY3Rpb24oZCxmLGcpewojICAgICBkaXIgPC0gc3ByaW50ZigiLi4vLi4vY292aWQvaXNiL3NjSExBY291bnQvZ2Vub3R5cGVzLyVzIixnKQojICAgICBpZiAoIWRpci5leGlzdHMoZGlyKSl7ZGlyLmNyZWF0ZShkaXIsIHJlY3Vyc2l2ZSA9IFQpfQojICAgICB3cml0ZV90c3YoZCwgCiMgICAgICAgICAgICAgICBzcHJpbnRmKCIlcy8lc19obGEudHN2IixkaXIsZiksIAojICAgICAgICAgICAgICAgY29sX25hbWVzID0gRiwKIyAgICAgICAgICAgICAgICl9KSkKYGBgCgoKIyMjIFJ1biBzY3JpcHQKCmBzYmF0Y2ggL2NvdmlkL3NjcmlwdHMvaXNiX3NjSExBY291bnRfYmVuY2htYXJrLnNoYAoKYGBge2Jhc2gsIGV2YWw9Rn0KIyEvYmluL3NoCiNTQkFUQ0ggLS1tYWlsLXR5cGU9RU5ELEZBSUwKI1NCQVRDSCAtLW1haWwtdXNlcj1zb2xvbW9uYkBzdGFuZm9yZC5lZHUKI1NCQVRDSCAtLXRpbWU9MTMtMjM6MDUgIyBSdW50aW1lIGluIEQtSEg6TU0KI1NCQVRDSCAtLWpvYi1uYW1lPVNDSExBCiNTQkFUQ0ggLS1ub2Rlcz0xICMgRW5zdXJlIHRoYXQgYWxsIGNvcmVzIGFyZSByZXNlcnZlZCBvbiBvbmUgbWFjaGluZQojU0JBVENIIC0tbnRhc2tzPTYKI1NCQVRDSCAtLWNwdXMtcGVyLXRhc2s9OAojU0JBVENIIC0tbWVtLXBlci1jcHU9N0cKI1NCQVRDSCAtLXBhcnRpdGlvbj1raGF0cmlsYWIgIyBQYXJ0aXRpb24gYWxsb2NhdGVkIGZvciB0aGUgbGFiCiNTQkFUQ0ggLS1lcnJvcj0uL3NsdXJtX2xvZ3MvJXguZXJyCiNTQkFUQ0ggLS1vdXRwdXQ9Li9zbHVybV9sb2dzLyV4Lm91dAoKIyBTRVQgR0xPQkFMIFZBUklBQkxFUwojIEdlbmVyYWwKZXhwb3J0IEJBU0VfRElSPS9sYWJzL2toYXRyaWxhYi9zb2xvbW9uYi9jb3ZpZC9pc2Ivc2NITEFjb3VudApleHBvcnQgTE9HX0RJUj0kQkFTRV9ESVIvbG9ncy8kKGRhdGUgKycleSVtJWRfJUglTSVTJykKIyBGQVNUUS9ISVNBVApleHBvcnQgSU5ERVhfRElSPS9sYWJzL2toYXRyaWxhYi9zb2xvbW9uYi9ybmFzZXFfcHJvY2Vzc2luZy9oaXNhdDIvaGlzYXRfYXJjYXMvaGlzYXRfZGF0YS9ncmNoMzgKZXhwb3J0IEJBTV9ESVI9JEJBU0VfRElSL2JhbQojIEhMQSByZWZlcmVuY2VzCmV4cG9ydCBITEFfRElSPSRCQVNFX0RJUi9obGFfcmVmZXJlbmNlcwpleHBvcnQgSExBTlVDPS9sYWJzL2toYXRyaWxhYi9zb2xvbW9uYi9yZWZlcmVuY2VzL0lNR1RITEEvaGxhX251Yy5mYXN0YQpleHBvcnQgSExBR0VOPS9sYWJzL2toYXRyaWxhYi9zb2xvbW9uYi9yZWZlcmVuY2VzL0lNR1RITEEvaGxhX2dlbi5mYXN0YQojIFNDSExBCmV4cG9ydCBCQVJDT0RFX0RJUj0kQkFTRV9ESVIvYmFyY29kZXMKZXhwb3J0IEdFTk9UWVBFX0RJUj0kQkFTRV9ESVIvZ2Vub3R5cGVzCmV4cG9ydCBTQ0hMQUNPVU5UX0RJUj0kQkFTRV9ESVIvb3V0cHV0CmV4cG9ydCBURU1QX0RJUj0kQkFTRV9ESVIvdGVtcF9mYXN0cQojIFNMVVJNIApleHBvcnQgTl9DT1JFUz0kU0xVUk1fQ1BVU19QRVJfVEFTSwoKCiMgQ1JFQVRFIERJUkVDVE9SSUVTCmlmIFsgISAtZCAkTE9HX0RJUiBdOyB0aGVuIG1rZGlyIC1wICRMT0dfRElSO2ZpCmlmIFsgISAtZCAkQkFNX0RJUiBdOyB0aGVuIG1rZGlyIC1wICRCQU1fRElSO2ZpCmlmIFsgISAtZCAkU0NITEFDT1VOVF9ESVIgXTsgdGhlbiBta2RpciAtcCAkU0NITEFDT1VOVF9ESVI7ZmkKaWYgWyAhIC1kICRURU1QX0RJUiBdOyB0aGVuIG1rZGlyIC1wICRURU1QX0RJUjtmaQoKIyBDUkVBVEUgSExBIFJFRkVSRUNFICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKSExBX1JFRkVSRU5DRSgpewogIHNvdXJjZSAvbGFicy9raGF0cmlsYWIvc29sb21vbmIvbWluaWNvbmRhMy9ldGMvcHJvZmlsZS5kL2NvbmRhLnNoCiAgY29uZGEgYWN0aXZhdGUgc2FtdG9vbHMKICAKICBwcmludGYgIlxuXAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyBDUkVBVEUgUkVGRVJFTkNFICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjI1wKICBcbiIgPj4gJExPR19ESVIvJHsxfS8kezF9XyR7Mn0ubG9nCiAgcHJpbnRmICJcbiMjIyBbU1RBUlRfX19SRUZFUkVOQ0VfX18kKGRhdGUgKyclRCAlWCcpXVxuIiA+PiAkTE9HX0RJUi8kezF9LyR7MX1fJHsyfS5sb2cKICAKICBbICEgLWQgJEhMQV9ESVIvJHsyfSBdICYmIG1rZGlyIC1wICRITEFfRElSLyR7Mn0KICAKICB3aGlsZSByZWFkIC1yIGxpbmU7IGRvIGdyZXAgLUYgLW0gMSAkbGluZSAkSExBTlVDID4+ICRITEFfRElSLyR7Mn0vJHsxfV90bXBhbGxlbGUudHh0OyBkb25lIDwgJEdFTk9UWVBFX0RJUi8kezJ9LyR7MX1faGxhLnRzdgogIAogIHNhbXRvb2xzIGZhaWR4ICAkSExBTlVDICQoY3V0IC1mMSAtZCcgJyAkSExBX0RJUi8kezJ9LyR7MX1fdG1wYWxsZWxlLnR4dCB8IHRyICc+JyAnICcgfCB0ciAnXG4nICcgJykgPiAkSExBX0RJUi8kezJ9LyR7MX1fY2RzLmZhc3RhIDI+PiAkTE9HX0RJUi8kezF9LyR7MX1fJHsyfS5sb2cKICBzYW10b29scyBmYWlkeCAgJEhMQUdFTiAkKGN1dCAtZjEgLWQnICcgJEhMQV9ESVIvJHsyfS8kezF9X3RtcGFsbGVsZS50eHQgfCB0ciAnPicgJyAnIHwgdHIgJ1xuJyAnICcpID4gJEhMQV9ESVIvJHsyfS8kezF9X2dlbi5mYXN0YSAyPj4gJExPR19ESVIvJHsxfS8kezF9XyR7Mn0ubG9nCiAgCiAgd2hpbGUgcmVhZCAtciBsaW5lOyBkbyBJRlM9JyAnOyByZWFkIC1yIGYxIGYyIDw8PCIkbGluZSI7IHNlZCAtaSIiICJzLyRmMS8kZjEgJGYyL2ciICRITEFfRElSLyR7Mn0vJHsxfV9jZHMuZmFzdGE7IGRvbmUgPCAkSExBX0RJUi8kezJ9LyR7MX1fdG1wYWxsZWxlLnR4dCAyPj4gJExPR19ESVIvJHsxfS8kezF9XyR7Mn0ubG9nCiAgd2hpbGUgcmVhZCAtciBsaW5lOyBkbyBJRlM9JyAnOyByZWFkIC1yIGYxIGYyIGYzIDw8PCIkbGluZSI7IHNlZCAtaSIiICJzLyRmMS8kZjEgJGYyL2ciICRITEFfRElSLyR7Mn0vJHsxfV9nZW4uZmFzdGE7IGRvbmUgPCAkSExBX0RJUi8kezJ9LyR7MX1fdG1wYWxsZWxlLnR4dCAyPj4gJExPR19ESVIvJHsxfS8kezF9XyR7Mn0ubG9nCiAgCiAgcm0gJEhMQV9ESVIvJHsyfS8kezF9X3RtcGFsbGVsZS50eHQKICBwcmludGYgIiMjIyBbQ09NUExFVEVfX19SRUZFUkVOQ0VfX18kKGRhdGUgKyclRCAlWCcpXVxuIiA+PiAkTE9HX0RJUi8kezF9LyR7MX1fJHsyfS5sb2cKfQpleHBvcnQgLWYgSExBX1JFRkVSRU5DRQoKCiMgREVGSU5FIHNjSExBIEdFTk9UWVBJTkcgUElQRUxJTkUgIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjClNDSExBQ09VTlQoKXsKICBzb3VyY2UgL2xhYnMva2hhdHJpbGFiL3NvbG9tb25iL21pbmljb25kYTMvZXRjL3Byb2ZpbGUuZC9jb25kYS5zaAogIGNvbmRhIGFjdGl2YXRlIHNhbXRvb2xzCiAgCiAgcHJpbnRmICJcblwKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgUlVOIFNDSExBQ09VTlQgIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjXAogIFxuIiA+PiAkTE9HX0RJUi8kezF9LyR7MX1fJHsyfS5sb2cKICBwcmludGYgIlxuIyMjIFtTVEFSVF9fX1NDSExBQ09VTlRfX18kKGRhdGUgKyclRCAlWCcpXVxuIiA+PiAkTE9HX0RJUi8kezF9LyR7MX1fJHsyfS5sb2cKICAKICBlY2hvICIjIyMgU3RhcnRpbmcgIHNjSExBY291bnQgYXQgJChkYXRlICsnJUQgJVgnKSIgPj4gJExPR19ESVIvJHsxfS8kezF9XyR7Mn0ubG9nCiAgCiAgIyBpZiBbIC1kICRTQ0hMQUNPVU5UX0RJUi8kezF9X3Jlc3VsdHMgXTsgdGhlbiBybSAtciAkU0NITEFDT1VOVF9ESVIvJHsxfV9yZXN1bHRzO2ZpCiAgWyAhIC1kIFNDSExBQ09VTlRfRElSLyR7Mn0gXSAmJiBta2RpciAtcCAkU0NITEFDT1VOVF9ESVIvJHsyfQoKICBzY19obGFfY291bnQgXAogIC0tYmFtICRCQU1fRElSLyR7MX0uYmFtIFwKICAtLWNlbGwtYmFyY29kZXMgJEJBUkNPREVfRElSLyR7MX1fYmFyY29kZS50c3YgXAogIC0tb3V0LWRpciAkU0NITEFDT1VOVF9ESVIvJHsyfS8kezF9X3Jlc3VsdHMgXAogIC0tZmFzdGEtY2RzICRITEFfRElSLyR7Mn0vJHsxfV9jZHMuZmFzdGEgXAogIC0tZmFzdGEtZ2Vub21pYyAkSExBX0RJUi8kezJ9LyR7MX1fZ2VuLmZhc3RhXAogID4+ICRMT0dfRElSLyR7MX0vJHsxfV8kezJ9LmxvZyBcCiAgMj4+ICRMT0dfRElSLyR7MX0vJHsxfV8kezJ9LmxvZwoKICBwcmludGYgIiMjIyBbQ09NUExFVEVfX19TQ0hMQUNPVU5UX19fJChkYXRlICsnJUQgJVgnKV1cbiIgPj4gJExPR19ESVIvJHsxfS8kezF9XyR7Mn0ubG9nCn0KZXhwb3J0IC1mIFNDSExBQ09VTlQKCgojIERFRklORSBQSVBFTElORSBDT05UUk9MTEVSIEZVTkNUSU9OICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpQSVBFTElORSgpewogIGVjaG8gIlNUQVJUOiBzYW1wbGUgJDEgYXQgJChkYXRlICsnJUQgJVgnKSIKICBmb3IgRyBpbiBhcmNhc0hMQSBobGFtaW5lciBpbnZpdHJvIG9wdGl0eXBlIHBobGF0CiAgZG8KICAgIFsgISAtZCAkTE9HX0RJUi8kezF9IF0gJiYgbWtkaXIgLXAgJExPR19ESVIvJHsxfQogICAgSExBX1JFRkVSRU5DRSAkMSAkRwogICAgU0NITEFDT1VOVCAkMSAkRwogIGRvbmUKICBlY2hvICJDT01QTEVURTogc2FtcGxlICQxIGF0ICQoZGF0ZSArJyVEICVYJykiCn0KZXhwb3J0IC1mIFBJUEVMSU5FCgpjYXQgJEJBU0VfRElSL0JMX2Zhc3RxX2ZpbGVzLnR4dCB8IHBhcmFsbGVsIC0tZGVsYXkgMTUgLWogJFNMVVJNX05UQVNLUyAtLWpvYmxvZyAkTE9HX0RJUi9wYXJhbGxlbC5sb2cgUElQRUxJTkUge30KYGBgCgoKIyBVTUFQIHByb2plY3Rpb25zCgojIyMgQ2VsbCBjbHVzdGVycwoKYGBge3J9CnNydCA8LSByZWFkUkRTKCIvbGFicy9raGF0cmlsYWIvaG9uZ3poZW5nL3dlYmFwcHMvaXNiL3NydF9pc2IyNjAubWV0YS5yZHMiKQpjZWxscyA8LSBjKCJDRDE0IE1vbm9jeXRlIiwgIkNENCBUIiwgIkNEOCBUIiwgIk5LIiwgIkIiLCAiQ0QxNiBNb25vY3l0ZSIsICJjREMiLCAicERDIikKIyBzcnQgPC0gc3J0ICU+JSBmaWx0ZXIoY2VsbHR5cGUgJWluJSBjZWxscykKYGBgCgpgYGB7cn0KIyBnZ3Bsb3QgdGhlbWUgdG8gb3ZlcmxheSBjbHVzdGVyIGlkICNzIG9uIFVNQVAKZ2dfc3J0X3JlbGFiZWwgPC0gZnVuY3Rpb24oZGYsIHhfdmFyLCB5X3ZhciwgY29sb3JfdmFyLCBjZWxsX2ZyYWN0aW9uID0gMSl7CiAgcGx0IDwtIGRmICU+JSAKICAgIGdncGxvdChhZXMoeCA9ICEhc3ltKHhfdmFyKSwgeSA9ICEhc3ltKHlfdmFyKSwgY29sb3IgPSAhIXN5bShjb2xvcl92YXIpKSkgKwogICAgZ2VvbV9wb2ludChzaXplID0gMC41LCBhbHBoYSA9IDAuNSkrCiAgICB0aGVtZV9idygpICsKICAgIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIkRhcmsyIikrCiAgICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gMiwgYWxwaGEgPSAxKSkpCiAgCiAgZyA8LSBnZ3Bsb3RfYnVpbGQocGx0KQogIAogIHBsdF9pZHMgPC0gZyRkYXRhW1sxXV0KICBncm91cF9sZXZlbHMgPC0gbGV2ZWxzKGZhY3RvcihnJHBsb3QkZGF0YVtbZyRwbG90JGxhYmVscyRjb2xvdXJdXSkpCiAgCiAgcGx0X2tleSA8LSBnJGRhdGFbWzFdXSAlPiUgCiAgICBzZWxlY3QoY29sb3VyLCBncm91cCkgJT4lIAogICAgZGlzdGluY3QoKSAlPiUgCiAgICBtdXRhdGUobGFiZWwgPSBtYXBfY2hyKGdyb3VwLCBmdW5jdGlvbih4KSBncm91cF9sZXZlbHNbeF0pKSAlPiUgCiAgICBtdXRhdGUobGFiZWwgPSBmYWN0b3IobGFiZWwsIGxldmVsID0gZ3JvdXBfbGV2ZWxzKSkgJT4lCiAgICBtdXRhdGUobGFiZWwgPSBzcHJpbnRmKCIlcykgJXMiLCAxOm4oKSwgbGFiZWwpKSAlPiUgCiAgICBzZWxlY3QoLWdyb3VwKQogIAogIHBsdF9kZiA8LSBwbHRfaWRzICU+JSAKICAgIGxlZnRfam9pbihwbHRfa2V5LCBieSA9ICJjb2xvdXIiKQogIAogIHBsdF9jZW50ZXIgPC0gcGx0X2RmICU+JSAKICAgIGdyb3VwX2J5KGxhYmVsKSAlPiUgc3VtbWFyaXNlKHggPSBtZWFuKHgpLCB5ID0gbWVhbih5KSkgJT4lCiAgICBtdXRhdGUobGFiZWwgPSBnc3ViKCIpLioiLCIiLGxhYmVsKSkKICAKICBwbHRfcmVwZWwgPC0gcGx0X2RmICU+JSAKICAgIGdncGxvdChhZXMoeD14LHk9eSxjb2xvcj1sYWJlbCkpICsKICAgIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSkrCiAgICBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwoZGF0YT1wbHRfY2VudGVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyhsYWJlbD1sYWJlbCwgIGJnLmNvbG9yPSJ3aGl0ZSIsIGJnLnI9MC4yNSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvbnRmYWNlID0gImJvbGQiKSArCiAgICB0aGVtZV9idygpICsKICAgIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSAyKSApICkgKwogICAgbGFicyh4PXhfdmFyLCB5ID0geV92YXIsIGNvbG9yID0gY29sb3JfdmFyKSArCiAgICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCkpCiAgcmV0dXJuKHBsdF9yZXBlbCkKfQoKcGx0X3NydCA8LSBzcnQgJT4lIAogIGZpbHRlcihjZWxsdHlwZSAlaW4lIGNlbGxzKSAlPiUgCiAgZmlsdGVyKHNhbXBsZUlEID09ICJJTkNPVjA2OS1CTCIpICU+JSAKICBnZ19zcnRfcmVsYWJlbCh4X3ZhciA9ICJVTUFQXzEiLCB5X3ZhciA9ICJVTUFQXzIiLCBjb2xvcl92YXIgPSAiY2VsbHR5cGUiLCBjZWxsX2ZyYWN0aW9uID0gMC4xKSArCiAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGUgPSAiRGFyazIiKSsgCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpKSArCiAgbGFicyh4PSJVTUFQIDEiLCB5ID0gIlVNQVAgMiIsIGNvbG9yID0gIkNlbGwgQ2x1c3RlciIpCnBsdF9zcnQKYGBgCiMjIyBBbGxlbGUgZnJlcXVlbmN5CgpgYGB7ciwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA2fQpzYW1wIDwtICJJTkNPVjA2OS1CTCIKIyBzYW1wIDwtICJJTkNPVjAyOC1CTCIKZ2VuZV9zZWxlY3QgPC0gIkEiCnNjSExBY291bnRfZGlyIDwtIHNwcmludGYoIiVzL3NjSExBY291bnQiLCBpc2JfcGF0aCkKCiMgQ3JlYXRlIHRpYmJsZSBvZiBhbGwgZ2Vub3R5cGVyIGFuZCBzYW1wbGUgY29tYmluYXRpb25zCmFsbGVsZV9kYXRhIDwtIGV4cGFuZF9ncmlkKAogIGdlbm90eXBlciA9IGMoImludml0cm8iLCAiYXJjYXNITEEiLCAib3B0aXR5cGUiLCAicGhsYXQiLCAiaGxhbWluZXIiKSwKICBzYW1wbGUgPSByZWFkX2xpbmVzKAogICAgIi9sYWJzL2toYXRyaWxhYi9zb2xvbW9uYi9jb3ZpZC9pc2Ivc2NITEFjb3VudC9CTF9mYXN0cV9maWxlcy50eHQiCiAgKSkgJT4lIAogIGZpbHRlcihncmVwbChzYW1wLCBzYW1wbGUpKSAlPiUgCiAgIyBJbXBvcnQgZGF0YSBiYXNlZCBvbiBzYW1wbGUgYW5kIGdlbm90eXBlcgogIG11dGF0ZShyZXN1bHRfcGF0aCA9IHNwcmludGYoIiVzL291dHB1dC8lcyIsc2NITEFjb3VudF9kaXIsIGdlbm90eXBlciksCiAgICAgICAgIGJhcmNvZGVfcGF0aCA9IHNwcmludGYoIiVzL2JhcmNvZGVzIiwgc2NITEFjb3VudF9kaXIpKSAlPiUgCiAgbXV0YXRlKGRhdGEgPSBwbWFwKGxpc3Qoc2FtcGxlLCByZXN1bHRfcGF0aCwgYmFyY29kZV9wYXRoKSwgZnVuY3Rpb24ocyxyLGIpewogICAgZGYgPC0gc2NITEFfZGF0YV9wcm9jZXNzaW5nKAogICAgICBzYW1wbGU9cywKICAgICAgcmVzdWx0X2Rpcj1yLAogICAgICBiYXJjb2RlX2Rpcj1iCiAgICApIAogIH0pKSAlPiUgdW5uZXN0KGRhdGEpCgoKYWxsZWxlX2RhdGFfcmF0aW9zIDwtIGFsbGVsZV9kYXRhICU+JSAKICBmaWx0ZXIoZ2VuZSA9PSBnZW5lX3NlbGVjdCkgJT4lIAogIG11dGF0ZShnZW5vdHlwZXIgPSByZWZvcm1hdF9obGFfZ2Vub3R5cGVyKGdlbm90eXBlcikpICU+JSAKICBtdXRhdGUoYWxsZWxlX29yZGVyID0gZmN0X3JlY29kZShmYWN0b3IoYWxsZWxlX29yZGVyKSwgIkFsbGVsZSAxIiA9ICIxIiwgIkFsbGVsZSAyIiA9ICIyIikpCmFsbGVsZV9kYXRhCiAgCmFsbGVsZV9sYWJlbCA8LSBhbGxlbGVfZGF0YV9yYXRpb3MgJT4lIAogIHNlbGVjdChnZW5vdHlwZXIsIGFsbGVsZV9vcmRlciwgYWxsZWxlKSAlPiUgCiAgZGlzdGluY3QoKQoKcGx0IDwtIHNydCAlPiUgCiAgbGVmdF9qb2luKGFsbGVsZV9kYXRhX3JhdGlvcyAlPiUgZmlsdGVyKGdlbmUgPT0gZ2VuZV9zZWxlY3QpLCBieSA9ICJjZWxsIikgJT4lIAogIGZpbHRlcighaXMubmEoYWxsZWxlKSkgJT4lIAogIGdncGxvdChhZXMoeCA9IFVNQVBfMSwgeSA9IFVNQVBfMiwgY29sb3IgPSBhbGxlbGVfcmF0aW8pKSArCiAgZ2VvbV9wb2ludChzaXplID0gMC41LCBhbHBoYSA9IDAuNSkrCiAgdGhlbWVfYncoKSArCiAgZmFjZXRfZ3JpZChhbGxlbGVfb3JkZXJ+Z2Vub3R5cGVyKSsKICBzY2FsZV9jb2xvcl9ncmFkaWVudDIoaGlnaCA9ICJyZWQiLCBtaWQgPSAiZ3JleTgwIiwgbG93ID0gImJsdWUiLCBtaWRwb2ludCA9IDAuNSwgbmEudmFsdWUgPSAidHJhbnNwYXJlbnQiKSArCiAgbGFicyh4PSJVTUFQIDEiLCB5PSJVTUFQIDIiLCBjb2xvciA9ICJBbGxlbGUgXG5GcmVxdWVuY3kiKSsKICB0aGVtZShheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRleHQgPSBlbGVtZW50X2JsYW5rKCkpKwogIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygtMTAsMTUpKQpwbHRfYWxsZWxlX2ZyZXEgPC0gcGx0KwogIGdlb21fbGFiZWwoZGF0YSA9IGFsbGVsZV9sYWJlbCwgYWVzKHg9LTEwLHk9MTQsIGNvbG9yID0gTlVMTCwgbGFiZWwgPSBhbGxlbGUpLCBzaXplID0gMywgaGp1c3QgPSAwKQpwbHRfYWxsZWxlX2ZyZXEKYGBgCgojIyMgTWF4aW11bSBhbGxlbGUKCmBgYHtyLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDMsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0KIyBzYW1wIDwtICJJTkNPVjA2OS1CTCIKIyBzYW1wIDwtICJJTkNPVjAyMi1CTCIKc2FtcCA8LSAiSU5DT1YwMjgtQkwiCiMgc2FtcCA8LSAiSU5DT1YwODMtQkwiCnNjSExBY291bnRfZGlyIDwtIHNwcmludGYoIiVzL3NjSExBY291bnQiLCBpc2JfcGF0aCkKCiMgQ3JlYXRlIHRpYmJsZSBvZiBhbGwgZ2Vub3R5cGVyIGFuZCBzYW1wbGUgY29tYmluYXRpb25zCmFsbGVsZV9kYXRhIDwtIGV4cGFuZF9ncmlkKAogIGdlbm90eXBlciA9IGMoImludml0cm8iLCAiYXJjYXNITEEiLCAib3B0aXR5cGUiLCAicGhsYXQiLCAiaGxhbWluZXIiKSwKICBzYW1wbGUgPSByZWFkX2xpbmVzKAogICAgIi9sYWJzL2toYXRyaWxhYi9zb2xvbW9uYi9jb3ZpZC9pc2Ivc2NITEFjb3VudC9CTF9mYXN0cV9maWxlcy50eHQiCiAgKSkgJT4lIAogIGZpbHRlcihncmVwbChzYW1wLCBzYW1wbGUpKSAlPiUgCiAgIyBJbXBvcnQgZGF0YSBiYXNlZCBvbiBzYW1wbGUgYW5kIGdlbm90eXBlcgogIG11dGF0ZShyZXN1bHRfcGF0aCA9IHNwcmludGYoIiVzL291dHB1dC8lcyIsc2NITEFjb3VudF9kaXIsIGdlbm90eXBlciksCiAgICAgICAgIGJhcmNvZGVfcGF0aCA9IHNwcmludGYoIiVzL2JhcmNvZGVzIiwgc2NITEFjb3VudF9kaXIpKSAlPiUgCiAgbXV0YXRlKGRhdGEgPSBwbWFwKGxpc3Qoc2FtcGxlLCByZXN1bHRfcGF0aCwgYmFyY29kZV9wYXRoKSwgZnVuY3Rpb24ocyxyLGIpewogICAgZGYgPC0gc2NITEFfZGF0YV9wcm9jZXNzaW5nKAogICAgICBzYW1wbGU9cywKICAgICAgcmVzdWx0X2Rpcj1yLAogICAgICBiYXJjb2RlX2Rpcj1iCiAgICApIAogIH0pKSAlPiUgdW5uZXN0KGRhdGEpCgojIENyZWF0ZSB0aWJibGUgb2YgYWxsIGdlbm90eXBlciBhbmQgc2FtcGxlIGNvbWJpbmF0aW9ucwphbGxlbGVfbWF4X3JhdGlvIDwtIGFsbGVsZV9kYXRhICAlPiUKICBzZWxlY3QoZ2Vub3R5cGVyLCBjZWxsLCBnZW5lLCBhbGxlbGVfb3JkZXIsIGFsbGVsZV9yYXRpbykgJT4lCiAgIyBncm91cF9ieShnZW5vdHlwZXIsIGNlbGwsIGFsbGVsZV9vcmRlcikgJT4lICBtdXRhdGUodGVzdCA9IGxlbmd0aChhbGxlbGVfb3JkZXIpKQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSAiYWxsZWxlX29yZGVyIiwgdmFsdWVzX2Zyb20gPSAiYWxsZWxlX3JhdGlvIiwgbmFtZXNfcHJlZml4ID0gImFsbGVsZV8iKSAlPiUKICByb3d3aXNlKCkgJT4lCiAgbXV0YXRlKG1heF9yYXRpbyA9IG1heChhY3Jvc3MoY29udGFpbnMoImFsbGVsZV8iKSksIG5hLnJtID0gVCkpICU+JQogIHNlbGVjdChnZW5vdHlwZXIsIGNlbGwsIGdlbmUsIG1heF9yYXRpbykgJT4lCiAgZmlsdGVyKGdlbmUgPT0gZ2VuZV9zZWxlY3QpIAoKYWxsZWxlX2xhYmVsIDwtIGFsbGVsZV9kYXRhICU+JSBzZWxlY3Qoc2FtcGxlLCBnZW5vdHlwZXIsIGdlbmUsIGFsbGVsZSkgJT4lIAogIGZpbHRlcihnZW5lID09IGdlbmVfc2VsZWN0KSAlPiUgCiAgc2VwYXJhdGUoc2FtcGxlLCBpbnRvID0gYygic2FtcGxlIiwgTlVMTCksIHNlcCA9ICJfIiwgZXh0cmEgPSAiZHJvcCIpICU+JSAKICBkaXN0aW5jdCgpICU+JSAKICBncm91cF9ieShzYW1wbGUsIGdlbm90eXBlciwgZ2VuZSkgJT4lIG5lc3QoKSAlPiUgdW5uZXN0X3dpZGVyKGRhdGEpICU+JSAKICBtdXRhdGUoZ2Vub3R5cGVyID0gcmVmb3JtYXRfaGxhX2dlbm90eXBlcihnZW5vdHlwZXIpKSAlPiUgCiAgbXV0YXRlKGFsbGVsZSA9IG1hcF9jaHIoYWxsZWxlLCBwYXN0ZSwgY29sbGFwc2UgPSAiXG4iKSkKCnBsdF9tYXhfYWxsZWxlIDwtIHNydCAlPiUgCiAgbGVmdF9qb2luKGFsbGVsZV9tYXhfcmF0aW8gJT4lIGZpbHRlcihnZW5lID09ICJBIiksIGJ5ID0gImNlbGwiKSAlPiUgCiAgIyBtdXRhdGUoZ2Vub3R5cGVyID0gZmN0X3JlbGV2ZWwoZ2Vub3R5cGVyLCAiaW52aXRybyIpKSAlPiUgCiAgbXV0YXRlKGdlbm90eXBlciA9IHJlZm9ybWF0X2hsYV9nZW5vdHlwZXIoZ2Vub3R5cGVyKSkgJT4lIAogIGZpbHRlcighaXMubmEoZ2Vub3R5cGVyKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gVU1BUF8xLCB5ID0gVU1BUF8yLCBjb2xvciA9IG1heF9yYXRpbykpICsKICAgIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSwgYWxwaGEgPSAwLjUpKwogICAgZ2VvbV9sYWJlbChkYXRhID0gYWxsZWxlX2xhYmVsLCBhZXMoY29sb3IgPSBOVUxMLCBsYWJlbCA9IGFsbGVsZSwgeCA9IC1JbmYsIHkgPSAtSW5mKSwKICAgICAgICAgICAgaGp1c3QgPSAtMC4xLCB2anVzdCA9IC0wLjEsIHNpemUgPSAzLCBoanVzdCA9IDApICsKICAgIHRoZW1lX2J3KCkgKwogICAgZmFjZXRfZ3JpZCgufmdlbm90eXBlcikrCiAgICBzY2FsZV9jb2xvcl9ncmFkaWVudChoaWdoID0gInJlZCIsIGxvdyA9ICJibHVlIiwgbmEudmFsdWUgPSAidHJhbnNwYXJlbnQiKSArCiAgICBsYWJzKHg9IlVNQVAgMSIsIHk9IlVNQVAgMiIsIGNvbG9yID0gIk1heGltdW0gQWxsZWxlIFxuRnJlcXVlbmN5IikrCiAgICB0aGVtZShheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRleHQgPSBlbGVtZW50X2JsYW5rKCkpCgpwbHRfbWF4X2FsbGVsZSAKYGBgCmBgYHtyfQpwbHRfbWF4X2FsbGVsZSA8LSBzcnQgJT4lIAogIGxlZnRfam9pbihhbGxlbGVfZGF0YSAlPiUgZmlsdGVyKGdlbmUgPT0gIkEiKSwgYnkgPSAiY2VsbCIpCnBsdF9tYXhfYWxsZWxlJGdlbm90eXBlciAlPiUgcmVmb3JtYXRfaGxhX2dlbm90eXBlcigpICU+JSBsZXZlbHMoKQpgYGAKCgoKIyBNZXRhLWFuYWx5c2lzIGFwcHJvYWNoCgojIyMgUmF0aW9uYWxlIAoKLSBHb2FsOiBkZXRlcm1pbmUgYSBzdW1tYXJ5IHN0YXRpc3RpYyBmb3IgYWxsZWxlIHJhdGlvIGFjcm9zcyBhbGwgY2VsbHMKLSBQcm9ibGVtOgogIC0gRWFjaCBjZWxsIGluIHNjUk5BIGV4cGVyaW1lbnQgaGFzIGl0cyBvd24gcmF0aW8gb2YgSExBIGFsbGVsZSAxIHJlYWRzIDogSExBIGFsbGVsZSAyIHJlYWRzCiAgLSBBIHdpZGUgcmFuZ2Ugb2YgdG90YWwgcmVhZCBjb3VudHMgbWF5IGdvIGludG8gZWFjaCBjZWxsJ3MgcmF0aW8KICAtIFNtYWxsIGRpZmZlcmVuY2UgaW4gcmVhZCBjb3VudHMgZm9yIGNlbGxzIHdpdGggbG93IHRvdGFsIHJlYWRzIGNhbiBncmVhdGx5IGFsdGVyIHRoZSBhbGxlbGUgcmF0aW8gY29tcGFyZWQgdG8gY2VsbHMgd2l0aCBoaWdoIHRvdGFsIHJlYWQgY291bnRzCiAgICAtIEkuZS4gQ2VsbHMgd2l0aCBsb3cgcmVhZCBjb3VudHMgYXJlIG1vcmUgbGlrZWx5IHRvIGhhdmUgaW1wcmVjaXNlIGFsbGVsZSByYXRpb3MKICAtIFNpbXBseSBhdmVyYWdpbmcgcmF0aW9zIHdvdWxkIGVxdWFsbHkgd2VpZ2h0IGhpZ2ggY29uZmlkZW5jZSwgaGlnaCByZWFkIGNvdW50IHJhdGlvcyB3aXRoIGxvdyBjb25maWRlbmNlLCBsb3cgcmVhZCByYXRpb3MKLSBBcHByb2FjaAogIC0gTWV0YS1hbmFseXNpcyBtZXRob2RzIHNwZWNpZmljYWxseSBhZGRyZXNzIHR5cGUgb2YgcHJvYmxlbSwgdHlwaWNhbGx5IGJ5IHdlaWdoaW5nIGFuIGVmZmVjdCBieSB0aGUgaW52ZXJzZSBvZiBpdHMgdmFyaWFuY2UKICAtIEluIHRoZSBzZXR0aW5nIG9mIHJhdGlvcywgYmFzaWMgYXBwcm9hY2ggd291bGQgYmUgdG8gY29tYmluZSBsb2ctb2RkcyByYXRpbyBvZiBhbGxlbGVzLCB3ZWlnaHRlZCBieSBpbnZlcnNlIHZhcmlhbmNlCiAgLSBXb3VsZCB1c2UgYSByYW5kb20gZWZmZWN0cyBtb2RlbCBiZWNhdXNlIGRvIG5vdCBleHBlY3QgZWFjaCBjZWxsIGluIGEgZ3JvdXAgd291bGQgaGF2ZSB0aGUgc2FtZSBleGFjdCByYXRpbyBkdWUgdG8gdmFyaW91cyBzdG9jaGFzdGljIHRyYW5zY3JpcHRpb25hbCBlZmZlY3RzCgojIyMgQ2FsY3VsYXRpbmcgc3VtbWFyeSBlZmZlY3Qgc2l6ZXMKCi0gQW5hbHlzaXMgY2FuIHRha2Ugc29tZSB0aW1lLCBzbyBydW4gaW4gc2x1cm0KLSBDb250YWluZWQgaW4gYSBzZXBhcmF0ZSBzY3JpcHQgYC4vc2x1cm0vc2NfbWV0YS5SYDoKCiMjIyMgQ29udGVudHMgb2YgYHNjX21ldGEuUmAKCmBgYHtyLCBldmFsID0gRn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkobWV0YSkKCnByb2plY3RfZGlyIDwtICIvbGFicy9raGF0cmlsYWIvc29sb21vbmIvaGxhX3Byb2plY3QvaGxhX2JlbmNobWFyayIKc291cmNlKHNwcmludGYoIiVzL2RhdGFfaW1wb3J0X2Z1bmN0aW9ucy5SIiwgcHJvamVjdF9kaXIpKQppc2JfcGF0aCA8LSAiL2xhYnMva2hhdHJpbGFiL3NvbG9tb25iL2NvdmlkL2lzYiIKc2NITEFjb3VudF9kaXIgPC0gc3ByaW50ZigiJXMvc2NITEFjb3VudCIsIGlzYl9wYXRoKQoKc3J0IDwtIHJlYWRSRFMoIi9sYWJzL2toYXRyaWxhYi9ob25nemhlbmcvd2ViYXBwcy9pc2Ivc3J0X2lzYjI2MC5tZXRhLnJkcyIpCmNlbGxzIDwtIGMoIkNEMTQgTW9ub2N5dGUiLCAiQ0Q0IFQiLCAiQ0Q4IFQiLCAiTksiLCAiQiIsICJDRDE2IE1vbm9jeXRlIiwgImNEQyIsICJwREMiKQoKIyBGdW5jdGlvbiB0byBwZXJmb3JtIG1ldGEtYW5hbHlzaXMgb24gZGF0YWZyYW1lIHdoZXJlCiMgZWFjaCByb3cgaXMgYSBjZWxsIGFuZCBjb2x1bW5zOgojIGBvYnNlcnZlZGAgKHJlYWRzIG9mIGRvbWluYW50IGFsbGVsZSkKIyBgZ2VuZV9zdW1fdHlwZWRgICh0b3RhbCBjZWxsIHJlYWRzKQojIGBleHBlY3RlZGAgKHJlYWRzIG9mIG9uZSBhbGxlbGUgZXhwZWN0ZWQgaWYgNTA6NTAgYWxsZWxlXzEgOiBhbGxlbGVfMikKc2NfbWV0YSA8LSBmdW5jdGlvbihkZil7CiAgbCA8LSBucm93KGRmKQogIG0gPC0gbWV0YWJpbihldmVudC5lID0gb2JzZXJ2ZWQsIG4uZSA9IGdlbmVfc3VtX3R5cGVkLCBldmVudC5jID0gZXhwZWN0ZWQsIG4uYyA9IGdlbmVfc3VtX3R5cGVkLAogICAgICAgICAgICAgICBkYXRhID0gZGYsCiAgICAgICAgICAgICAgIG1ldGhvZCA9ICJJbnZlcnNlIiwKICAgICAgICAgICAgICAgaW5jciA9IDAuMSwKICAgICAgICAgICAgICAgc20gPSAiT1IiKQogIGRhdGEuZnJhbWUoc3VtbWFyeShtKSRyYW5kb20pICU+JSAKICAgIG11dGF0ZShuX2NlbGxzID0gbCkgJT4lIAogICAgc2VsZWN0KFRFLCBzZVRFLCBsb3dlciwgdXBwZXIsIG5fY2VsbHMpIAp9CgojIEltcG9ydCBkYXRhIGJhc2VkIG9uIHNhbXBsZSBhbmQgZ2Vub3R5cGVyCmNlbGxfc3RhdHMgPC0gZXhwYW5kX2dyaWQoCiAgZ2Vub3R5cGVyID0gYygiaW52aXRybyIsICJhcmNhc0hMQSIsICJvcHRpdHlwZSIsICJwaGxhdCIsICJobGFtaW5lciIpLAogIHNhbXBsZSA9IHJlYWRfbGluZXMoCiAgICAiL2xhYnMva2hhdHJpbGFiL3NvbG9tb25iL2NvdmlkL2lzYi9zY0hMQWNvdW50L0JMX2Zhc3RxX2ZpbGVzLnR4dCIKICApKSAlPiUKICAjIGhlYWQoNSkgJT4lICMgU3BlY2lmeSBsaW1pdCB0byBudW1iZXIgb2YgbWV0YS1hbmFseXNlcwogIG11dGF0ZShkYXRhID0gbWFwMihzYW1wbGUsIGdlbm90eXBlciwgZnVuY3Rpb24ocyxnKXsKICAgIHJlc3VsdF9wYXRoID0gc3ByaW50ZigiJXMvb3V0cHV0LyVzIiwgc2NITEFjb3VudF9kaXIsIGcpCiAgICBiYXJjb2RlX3BhdGggPSBzcHJpbnRmKCIlcy9iYXJjb2RlcyIsIHNjSExBY291bnRfZGlyKQogICAgc2NITEFfZGF0YV9wcm9jZXNzaW5nKHNhbXBsZSA9IHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0X2RpciA9IHJlc3VsdF9wYXRoLAogICAgICAgICAgICAgICAgICAgICAgICAgIGJhcmNvZGVfZGlyID0gYmFyY29kZV9wYXRoKQogIH0pKSAlPiUgdW5uZXN0KGRhdGEpICU+JSAKICBmaWx0ZXIoIWlzLm5hKGNlbGwpKSAlPiUgCiAgbXV0YXRlKHNhbXBsZSA9IGdzdWIoIl9bQS1aXVswLTldJCIsIiIsc2FtcGxlKSkgJT4lICMgQ29uc29saWRhdGUgc2FtcGxlcwogIGxlZnRfam9pbihzcnQgJT4lIHNlbGVjdChjZWxsdHlwZSwgY2VsbCksIGJ5ID0gImNlbGwiKSAlPiUgIyBBZGQgY2VsbHR5cGVzCiAgZmlsdGVyKGNlbGx0eXBlICVpbiUgY2VsbHMpICMgS2VlcCBvbmx5IHN0YW5kYXJkIGNlbGwgdHlwZXMKCgptZXRhX2RmIDwtIGNlbGxfc3RhdHMgJT4lIAogICMgS2VlcCBvbmx5IG1vc3QgZXhwcmVzc2VkIGFsbGVsZSAob3IgcmFuZG9tIGlmIDUwOjUwKQogIGdyb3VwX2J5KHNhbXBsZSwgZ2Vub3R5cGVyLCBnZW5lLCBjZWxsKSAlPiUgCiAgc2xpY2VfbWF4KG9yZGVyX2J5ID0gYWxsZWxlX3JhdGlvLCBuID0gMSwgd2l0aF90aWVzID0gRikgJT4lIAogIHVuZ3JvdXAoKSAlPiUgCiAgIyBGaWxsIG91dCBjb250aW5nZW5jeSB0YWJsZQogIG11dGF0ZShjb21wbGVtZW50ID0gZ2VuZV9zdW1fdHlwZWQgLSBjb3VudCwgCiAgICAgICAgIGV4cGVjdGVkID0gMC41KmdlbmVfc3VtX3R5cGVkKSAlPiUgCiAgcmVuYW1lKG9ic2VydmVkID0gY291bnQpICU+JSAKICBzZWxlY3Qoc2FtcGxlLCBnZW5vdHlwZXIsIGNlbGx0eXBlLCBnZW5lLCBvYnNlcnZlZCwgZXhwZWN0ZWQsIGdlbmVfc3VtX3R5cGVkKSAlPiUgCiAgZ3JvdXBfYnkoc2FtcGxlLCBnZW5vdHlwZXIsIGNlbGx0eXBlLCBnZW5lKSAlPiUKICAjIE5lc3QgYW5kIHJ1biBtZXRhLWFuYWx5c2lzCiAgbmVzdCgpICU+JQogIHVuZ3JvdXAoKSAlPiUgCiAgbXV0YXRlKGRhdGEgPSBtYXAoZGF0YSxmdW5jdGlvbih4KSB7c2NfbWV0YSh4KX0pKSAlPiUKICB1bm5lc3QoZGF0YSkKCndyaXRlX2NzdihtZXRhX2RmLCBzcHJpbnRmKCIlcy9zbHVybS9tZXRhX2FuYWx5c2lzX3Jlc3VsdHMuY3N2IiwgcHJvamVjdF9kaXIpKQpgYGAKCiMjIyBTaW5nbGUgc2FtcGxlIGFuYWx5c2lzCgpgYGB7ciwgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9Cm1ldGFfZGYgPC0gcmVhZF9jc3YoIi4vc2x1cm0vbWV0YV9hbmFseXNpc19yZXN1bHRzLmNzdiIpIAptZXRhX2RmCmBgYApgYGB7cn0KIyBTaW5nbGUgY2VsbHR5cGUgYW5kIGdlbmUKIyBzYW1wIDwtICJJTkNPVjA2OS1CTCIKIyBzYW1wIDwtICJJTkNPVjAyMi1CTCIKc2FtcCA8LSAiSU5DT1YwMjgtQkwiCiMgc2FtcCA8LSAiSU5DT1YwODMtQkwiCnBsdF9tZXRhIDwtIG1ldGFfZGYgJT4lIAogIGZpbHRlcihzYW1wbGUgPT0gc2FtcCkgJT4lIAogIGZpbHRlcihjZWxsdHlwZSA9PSAiTksiLCBnZW5lID09ICJBIikgJT4lIAogIG11dGF0ZShnZW5vdHlwZXIgPSByZWZvcm1hdF9obGFfZ2Vub3R5cGVyKGdlbm90eXBlcikpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBnZW5vdHlwZXIsIHkgPSBURSwgeW1pbiA9IGxvd2VyLCB5bWF4ID0gdXBwZXIpKSsKICBnZW9tX3BvaW50KCkrCiAgZ2VvbV9lcnJvcmJhcigpKwogIHRoZW1lX2J3KCkrCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCxOQSkpKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpICsKICBsYWJzKHkgPSAiTG9nLU9kZHMgcmF0aW8gb2YgZG9taW5hbnQgYWxsZWxlIiwgeD0gTlVMTCkKcGx0X21ldGEKYGBgCmBgYHtyfQpzYW1wIDwtICJJTkNPVjA2OS1CTCIKc2FtcGxlcyA8LSB1bmlxdWUobWV0YV9kZiRzYW1wbGUpCmZvciAoaSBpbiAxOmxlbmd0aChzYW1wbGVzKSl7CnBsdF9tZXRhIDwtIG1ldGFfZGYgJT4lIAogIGZpbHRlcihzYW1wbGUgPT0gc2FtcGxlc1tpXSkgJT4lIAogIGZpbHRlcihjZWxsdHlwZSA9PSAiQ0QxNCBNb25vY3l0ZSIsIGdlbmUgPT0gIkEiKSAlPiUgCiAgbXV0YXRlKGdlbm90eXBlciA9IHJlZm9ybWF0X2hsYV9nZW5vdHlwZXIoZ2Vub3R5cGVyKSkgJT4lIAogIGdncGxvdChhZXMoeCA9IGdlbm90eXBlciwgeSA9IFRFLCB5bWluID0gbG93ZXIsIHltYXggPSB1cHBlcikpKwogIGdlb21fcG9pbnQoKSsKICBnZW9tX2Vycm9yYmFyKCkrCiAgdGhlbWVfYncoKSsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLE5BKSkrCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkgKwogIGxhYnMoeSA9ICJMb2ctT2RkcyByYXRpbyBvZiBkb21pbmFudCBhbGxlbGUiLCB4PSBOVUxMKSArCiAgZ2d0aXRsZShzYW1wbGVzW2ldKQogIHByaW50KHBsdF9tZXRhKQp9CmBgYAoKCgpgYGB7ciwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA0fQojIEFsbCBjZWxsdHlwZXMgYW5kIGdlbmVzCm1ldGFfZGYgJT4lIAogIG11dGF0ZSgKICAgIGNlbGx0eXBlID0gZmFjdG9yKGNlbGx0eXBlLCBsZXZlbHMgPSBjKAogICAgImNEQyIsICJDRDE0IE1vbm9jeXRlIiwgIkIiLCAicERDIiwgIkNEMTYgTW9ub2N5dGUiLCAiQ0Q4IFQiLCAiQ0Q0IFQiLCAiTksiKSkpICU+JSAKICBtdXRhdGUoZ2Vub3R5cGVyID0gcmVmb3JtYXRfaGxhX2dlbm90eXBlcihnZW5vdHlwZXIpKSAlPiUgCiAgbXV0YXRlKGdlbmUgPSByZWZvcm1hdF9obGFfbG9jaShnZW5lKSkgJT4lIAogIGdncGxvdChhZXMoeCA9IGNlbGx0eXBlLCB5ID0gVEUsIHltaW4gPSBsb3dlciwgeW1heCA9IHVwcGVyLCBmaWxsID0gY2VsbHR5cGUpKSsKICAgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIsIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDAuMjUpKwogICAgZ2VvbV9lcnJvcmJhcihwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuOSksIHdpZHRoID0wLjUpKwogICAgdGhlbWVfYncoKSsKICAgIGZhY2V0X2dyaWQoZ2VuZX5nZW5vdHlwZXIsIHNjYWxlcyA9ICJmcmVlX3kiKSsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpICsKICAgIGxhYnMoeSA9ICJMb2ctT2RkcyByYXRpbyBvZiBkb21pbmFudCBhbGxlbGUiLCB4PSBOVUxMLCBmaWxsID0gIkNlbGwgdHlwZSIpICsKICAgIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiRGFyazIiKQoKbWV0YV9kZiAlPiUgCiAgbXV0YXRlKAogICAgY2VsbHR5cGUgPSBmYWN0b3IoY2VsbHR5cGUsIGxldmVscyA9IGMoCiAgICAiY0RDIiwgIkNEMTQgTW9ub2N5dGUiLCAiQiIsICJwREMiLCAiQ0QxNiBNb25vY3l0ZSIsICJDRDggVCIsICJDRDQgVCIsICJOSyIpKSkgJT4lIAogIG11dGF0ZShnZW5vdHlwZXIgPSByZWZvcm1hdF9obGFfZ2Vub3R5cGVyKGdlbm90eXBlcikpICU+JSAKICBtdXRhdGUoZ2VuZSA9IHJlZm9ybWF0X2hsYV9sb2NpKGdlbmUpKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gZ2Vub3R5cGVyLCB5ID0gVEUsIHltaW4gPSBsb3dlciwgeW1heCA9IHVwcGVyLCBmaWxsID0gY2VsbHR5cGUpKSsKICAgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIsIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDAuMjUpKwogICAgZ2VvbV9lcnJvcmJhcihwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuOSksIHdpZHRoID0wLjUpKwogICAgdGhlbWVfYncoKSsKICAgIGZhY2V0X2dyaWQoZ2VuZX5jZWxsdHlwZSwgc2NhbGVzID0gImZyZWVfeSIpKwogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkgKwogICAgbGFicyh5ID0gIkxvZy1PZGRzIHJhdGlvIG9mIGRvbWluYW50IGFsbGVsZSIsIHg9IE5VTEwsIGZpbGwgPSAiQ2VsbCB0eXBlIikgKwogICAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJEYXJrMiIpCmBgYAoKCgojIyMgTXVsdGktc2FtcGxlIGFuYWx5c2lzCgpgYGB7ciwgbWVzc2FnZSA9IEZ9Cm1ldGFfZGYgPC0gcmVhZF9jc3YoIi4vc2x1cm0vbWV0YV9hbmFseXNpc19yZXN1bHRzLmNzdiIpICU+JSAKICBtdXRhdGUoZ2Vub3R5cGVyID0gcmVmb3JtYXRfaGxhX2dlbm90eXBlcihnZW5vdHlwZXIpLAogICAgICAgICBjZWxsdHlwZSA9IGZhY3RvcihjZWxsdHlwZSwgbGV2ZWxzID0gYygKICAgICAgICAgICJjREMiLCAiQ0QxNCBNb25vY3l0ZSIsICJCIiwgInBEQyIsICJDRDE2IE1vbm9jeXRlIiwgIkNEOCBUIiwgIkNENCBUIiwgIk5LIikpKQpgYGAKCmBgYHtyLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDYsIHdhcm5pbmcgPSBGfQptZXRhX2RmICU+JSAKICBnZ3Bsb3QoYWVzKHg9Y2VsbHR5cGUseT1URSwgZmlsbCA9IGNlbGx0eXBlKSkrCiAgZ2VvbV92aW9saW4oKSsKICBnZW9tX2ppdHRlcihhbHBoYSA9IDAuMiwgc2l6ZSA9IDAuMikrCiAgc3RhdF9zdW1tYXJ5KGZ1bj1tZWFuLCBzdGF0PSAicG9pbnQiKSsKICBzdGF0X3N1bW1hcnkoZnVuLmRhdGEgPSBtZWFuX3NlLCBnZW9tPSJlcnJvcmJhciIpKwogIGZhY2V0X2dyaWQoZ2VuZX5nZW5vdHlwZXIpKwogIHRoZW1lX2J3KCkrCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkgKwogIGxhYnMoeSA9ICJMb2ctT2RkcyByYXRpbyBvZiBkb21pbmFudCBhbGxlbGUiLCB4PSBOVUxMLCBmaWxsID0gIkNlbGwgdHlwZSIpICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIkRhcmsyIikKCm1ldGFfZGYgJT4lIAogIGdncGxvdChhZXMoeD1nZW5vdHlwZXIseT1URSwgZmlsbCA9IGNlbGx0eXBlKSkrCiAgZ2VvbV92aW9saW4oKSsKICBnZW9tX2ppdHRlcihhbHBoYSA9IDAuMiwgc2l6ZSA9IDAuMikrCiAgc3RhdF9zdW1tYXJ5KGZ1bj1tZWFuLCBzdGF0PSAicG9pbnQiKSsKICBzdGF0X3N1bW1hcnkoZnVuLmRhdGEgPSBtZWFuX3NlLCBnZW9tPSJlcnJvcmJhciIpKwogIGZhY2V0X2dyaWQoZ2VuZX5jZWxsdHlwZSkrCiAgdGhlbWVfYncoKSsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSArCiAgbGFicyh5ID0gIkxvZy1PZGRzIHJhdGlvIG9mIGRvbWluYW50IGFsbGVsZSIsIHg9IE5VTEwsIGZpbGwgPSAiQ2VsbCB0eXBlIikgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiRGFyazIiKQpgYGAKCiMjIyBDb3JyZWxhdGlvbiBhbmFseXNpcwoKYGBge3IsIG1lc3NhZ2UgPSBGLCB3YXJuaW5nPUZ9Cm1ldGFfZGYgJT4lIAogIGZpbHRlcihnZW5lID09ICJBIiwgY2VsbHR5cGUgPT0gImNEQyIpICU+JSAKICBzZWxlY3Qoc2FtcGxlLCBnZW5vdHlwZXIsIFRFKSAlPiUgCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9ICJnZW5vdHlwZXIiLCB2YWx1ZXNfZnJvbSA9ICJURSIpICU+JSAKICBzZWxlY3QoLXNhbXBsZSkgJT4lIAogIEdHYWxseTo6Z2dwYWlycyhwcm9ncmVzcyA9IEZBTFNFKSArCiAgdGhlbWVfYncoKQpgYGAKCmBgYHtyLCBtZXNzYWdlID0gRiwgd2FybmluZz1GfQphY2N1cmFjeV9kZiA8LSByZWFkUkRTKCJpc2JfYWNjdXJhY3lfZHJiMzQ1X2ZpbHRlcmVkLlJEUyIpICU+JSAKICBtdXRhdGUoZ2Vub3R5cGVyID0gcmVmb3JtYXRfaGxhX2dlbm90eXBlcihnZW5vdHlwZXIpKSAlPiUgCiAgc2VsZWN0KHNhbXBsZSwgZ2VuZT1sb2N1cywgZ2Vub3R5cGVyLCBhY2N1cmFjeSkgJT4lIAogIGRpc3RpbmN0KCkKZm9yIChpIGluIGMoMCwxKSl7CiAgc3VwcHJlc3NXYXJuaW5ncyh7CiAgcGx0IDwtIG1ldGFfZGYgJT4lIAogICAgdW5ncm91cCgpICU+JSAKICAgIGZpbHRlcihnZW5lID09ICJBIiwgY2VsbHR5cGUgPT0gImNEQyIpICU+JSAKICAgIGxlZnRfam9pbihhY2N1cmFjeV9kZiwgYnkgPSBjKCJzYW1wbGUiLCAiZ2VuZSIsICJnZW5vdHlwZXIiKSkgJT4lIAogICAgZmlsdGVyKGFjY3VyYWN5ID09IGkgfCBnZW5vdHlwZXIgPT0gIkdyb3VuZCB0cnV0aCIpICU+JSAKICAgICMgZHJvcF9uYShhY2N1cmFjeSkgJT4lIAogICAgc2VsZWN0KHNhbXBsZSwgZ2Vub3R5cGVyLCBURSkgJT4lIAogICAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9ICJnZW5vdHlwZXIiLCB2YWx1ZXNfZnJvbSA9ICJURSIpICU+JSAKICAgIHNlbGVjdCgtc2FtcGxlKSAlPiUgCiAgICBHR2FsbHk6OmdncGFpcnMocHJvZ3Jlc3MgPSBGQUxTRSkgKwogICAgdGhlbWVfYncoKSArCiAgICBnZ3RpdGxlKHNwcmludGYoIkNvcnJlbGF0aW9uIG9mIGFsbGVsZSByYXRpb3Mgd2hlcmUgYWNjdXJhY3kgPSAlcyIsIGkpKQogIHByaW50KHBsdCkKICB9KQp9CmBgYAoKYGBge3IsIHdhcm5pbmcgPSBGfQpjb3JyX2RmIDwtIG1ldGFfZGYgJT4lIAogIG11dGF0ZShnZW5vdHlwZXIgPSByZWZvcm1hdF9obGFfZ2Vub3R5cGVyKGdlbm90eXBlcikpICU+JSAKICBzZWxlY3Qoc2FtcGxlLCBnZW5vdHlwZXIsIGNlbGx0eXBlLCBnZW5lLCBURSkgJT4lIAogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSAiZ2Vub3R5cGVyIiwgdmFsdWVzX2Zyb20gPSAiVEUiKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGMoYXJjYXNITEEsIEhMQW1pbmVyLCBPcHRpVHlwZSwgUEhMQVQpLCBuYW1lc190byA9ICJnZW5vdHlwZXIiLCB2YWx1ZXNfdG8gPSAiVEUiKSAlPiUgCiAgbXV0YXRlKGdlbm90eXBlciA9IHJlZm9ybWF0X2hsYV9nZW5vdHlwZXIoZ2Vub3R5cGVyKSkKY29ycl9kZiAlPiUgCiAgZ2dwbG90KGFlcyh4PWBHcm91bmQgdHJ1dGhgLCB5PVRFKSkrCiAgZ2VvbV9wb2ludChzaXplID0gMC41KSsKICBmYWNldF9ncmlkKGdlbmUgfiBnZW5vdHlwZXIpICsKICB0aGVtZV9idygpICsKICBnZ3B1YnI6OnN0YXRfY29yKGFlcyhsYWJlbCA9IC4ucnIubGFiZWwuLiksIGxhYmVsLngubnBjID0gImxlZnQiLCBsYWJlbC55Lm5wYyA9ICJ0b3AiLCBnZW9tID0gImxhYmVsIikKYGBgCgpgYGB7cn0KZm9yIChpIGluIGMoMCwxKSl7CiAgc3VwcHJlc3NXYXJuaW5ncyh7CiAgcGx0IDwtIGNvcnJfZGYgJT4lIAogICAgdW5ncm91cCgpICU+JSAKICAgICMgZmlsdGVyKGdlbmUgPT0gIkEiLCBjZWxsdHlwZSA9PSAiY0RDIikgJT4lIAogICAgbGVmdF9qb2luKGFjY3VyYWN5X2RmLCBieSA9IGMoInNhbXBsZSIsICJnZW5lIiwgImdlbm90eXBlciIpKSAlPiUgCiAgICBmaWx0ZXIoYWNjdXJhY3kgPT0gaSkgJT4lIAogICAgZ2dwbG90KGFlcyh4PWBHcm91bmQgdHJ1dGhgLCB5PVRFKSkrCiAgICBnZW9tX3BvaW50KHNpemUgPSAwLjUpKwogICAgZmFjZXRfZ3JpZChnZW5lIH4gZ2Vub3R5cGVyKSArCiAgICB0aGVtZV9idygpICsKICAgIGdncHVicjo6c3RhdF9jb3IoYWVzKGxhYmVsID0gLi5yci5sYWJlbC4uKSwgbGFiZWwueC5ucGMgPSAibGVmdCIsIGxhYmVsLnkubnBjID0gInRvcCIsIGdlb20gPSAibGFiZWwiKSArCiAgICAgIGdndGl0bGUoc3ByaW50ZigiQ29ycmVsYXRpb24gb2YgYWxsZWxlIHJhdGlvcyB3aGVyZSBhY2N1cmFjeSA9ICVzIiwgaSkpICsKICAgIGxhYnMoeSA9ICJQcmVkaWN0ZWQgZ2Vub3R5cGUgSExBIGFsbGVsZSBsb2ctb2RkcyByYXRpbyIsIHggPSAiR3JvdW5kIHRydXRoIGdlbm90eXBlIEhMQSBhbGxlbGUgbG9nLW9kZHMgcmF0aW8iKQogIGFzc2lnbihzcHJpbnRmKCJwbHRfbWV0YV9jb3JyXyVzIiwgaSksIHBsdCkKICBwcmludChwbHQpCiAgfSkKfQoKYGBgCmBgYHtyfQpwbHRfbWV0YV9jb3JyX2FiYnJldiA8LSBjb3JyX2RmICU+JSAKICBsZWZ0X2pvaW4oYWNjdXJhY3lfZGYsIGJ5ID0gYygic2FtcGxlIiwgImdlbmUiLCAiZ2Vub3R5cGVyIikpICU+JSAKICBmaWx0ZXIoZ2VuZSA9PSAiQSIsIGFjY3VyYWN5ICVpbiUgYygwLDAuNSwxKSkgJT4lCiAgZ2dwbG90KGFlcyh4PWBHcm91bmQgdHJ1dGhgLCB5PVRFKSkrCiAgICBnZW9tX3BvaW50KHNpemUgPSAwLjUpKwogICAgZmFjZXRfZ3JpZChhY2N1cmFjeSB+IGdlbm90eXBlciwgbGFiZWxsZXIgPSBsYWJlbGxlcihhY2N1cmFjeSA9IGZ1bmN0aW9uKHgpIHNwcmludGYoIkFjY3VyYWN5OiAlcyIsIHgpKSkgKwogICAgdGhlbWVfYncoKSArCiAgICBnZ3B1YnI6OnN0YXRfY29yKGFlcyhsYWJlbCA9IC4ucnIubGFiZWwuLiksIGxhYmVsLngubnBjID0gImxlZnQiLCBsYWJlbC55Lm5wYyA9ICJ0b3AiLCBnZW9tID0gImxhYmVsIiwgbWV0aG9kID0gInBlYXJzb24iKSArCiAgICBsYWJzKHkgPSAiTG9nLW9kZHMgcmF0aW8gdXNpbmcgcHJlZGljdGVkIGdlbm90eXBlIiwgeCA9ICJMb2ctb2RkcyByYXRpbyB1c2luZyBncm91bmQgdHJ1dGggZ2Vub3R5cGVyIikKcGx0X21ldGFfY29ycl9hYmJyZXYKYGBgCmBgYHtyfQphY2N1cmFjeV9kZiAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBkcm9wX25hKCkgJT4lIAogIGNvdW50KGdlbmUsIGdlbm90eXBlciwgYWNjdXJhY3kpCmBgYAoKCgojIyMgQnkgc2V2ZXJpdHkKCmBgYHtyLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDYsIHdhcm5pbmcgPSBGfQptZXRhX2RmICU+JSAKICBmaWx0ZXIoZ2Vub3R5cGVyID09ICJHcm91bmQgdHJ1dGgiKSAlPiUgCiAgbGVmdF9qb2luKAogICAgc3J0ICU+JSAKICAgICAgc2VsZWN0KHNhbXBsZSA9IHNhbXBsZUlELCBzZXZlcml0eSkgJT4lIAogICAgICBkaXN0aW5jdCgpLAogICAgYnkgPSAic2FtcGxlIgogICkgJT4lIAogIGdncGxvdChhZXMoeD1zZXZlcml0eSx5PVRFLCBmaWxsID0gY2VsbHR5cGUpKSsKICAgIGdlb21fdmlvbGluKCkrCiAgICBnZW9tX2ppdHRlcihhbHBoYSA9IDAuMiwgc2l6ZSA9IDAuMikrCiAgICBzdGF0X3N1bW1hcnkoZnVuPW1lYW4sIHN0YXQ9ICJwb2ludCIpKwogICAgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gbWVhbl9zZSwgZ2VvbT0iZXJyb3JiYXIiKSsKICAgIGZhY2V0X2dyaWQoZ2VuZX5jZWxsdHlwZSkrCiAgICB0aGVtZV9idygpKwogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkgKwogICAgbGFicyh5ID0gIkxvZy1PZGRzIHJhdGlvIG9mIGRvbWluYW50IGFsbGVsZSIsIHg9IE5VTEwsIGZpbGwgPSAiQ2VsbCB0eXBlIikgKwogICAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJEYXJrMiIpCmBgYAoKYGBge3J9Cm1ldGFfZGYgJT4lIAogIGZpbHRlcihnZW5vdHlwZXIgPT0gIkdyb3VuZCB0cnV0aCIpICU+JSAKICBsZWZ0X2pvaW4oCiAgICBzcnQgJT4lIAogICAgICBzZWxlY3Qoc2FtcGxlID0gc2FtcGxlSUQsIHNldmVyaXR5KSAlPiUgCiAgICAgIGRpc3RpbmN0KCksCiAgICBieSA9ICJzYW1wbGUiCiAgKSAlPiUgCiAgbXV0YXRlKHNldmVyaXR5ID0gYXMubnVtZXJpYyhzZXZlcml0eSkpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBzZXZlcml0eSwgeSA9IFRFKSkgKwogIGdlb21fcG9pbnQoKSArIAogIHN0YXRfc21vb3RoKG1ldGhvZCA9ICJsbSIpKwogIGZhY2V0X3dyYXAofmNlbGx0eXBlKSsKICB0aGVtZV9idygpIApgYGAKCiMgUGxvdHMgZm9yIGZpZ3VyZXMKCiMgQXNzZW1ibGUgcGxvdApgYGB7ciwgZmlnLndpZHRoID0gMTQsIGZpZy5oZWlnaHQgPSAxMH0KIyBwbHRfYWxsZWxlX2ZyZXFfbGdkIDwtIGNvd3Bsb3Q6OmdldF9sZWdlbmQocGx0X2FsbGVsZV9mcmVxKQojIHBsdF9tYXhfYWxsZWxlX2xnZCA8LSBjb3dwbG90OjpnZXRfbGVnZW5kKHBsdF9tYXhfYWxsZWxlKQpjb2xfMSA8LSBwbG90X2dyaWQoCiAgcGx0X2FsbGVsZV9mcmVxLAogIHBsdF9tYXhfYWxsZWxlLAogIG5jb2wgPSAxLAogIHJlbF9oZWlnaHRzID0gYyg1LDMpLAogIGFsaWduID0gInYiLCBheGlzID0gImxyIiwKICBsYWJlbHMgPSBMRVRURVJTWzE6Ml0KKQoKY29sXzIgPC0gcGxvdF9ncmlkKAogIHBsdF9zcnQsCiAgcGx0X21ldGEsCiAgbmNvbCA9IDEsCiAgYWxpZ24gPSAidiIsIGF4aXMgPSAibHIiLAogIGxhYmVscyA9IExFVFRFUlNbMzo0XSwKICBoanVzdCA9IDAuNQopCnJvd18xIDwtIHBsb3RfZ3JpZCgKICBjb2xfMSwKICBjb2xfMiwKICBucm93ID0gMSwKICByZWxfd2lkdGhzID0gYyg2LDMpCikKcm93XzIgPC0gcGxvdF9ncmlkKAogIHBsdF9tZXRhX2NvcnJfMCArZ2d0aXRsZShOVUxMKSwKICBwbHRfbWV0YV9jb3JyXzEgK2dndGl0bGUoTlVMTCksCiAgbGFiZWxzID0gTEVUVEVSU1s1OjZdLAogIG5jb2wgPSAyCikKcGxvdF9ncmlkKAogIHJvd18xLAogIHJvd18yLAogIHJlbF9oZWlnaHRzID0gYygzLDIpLAogIG5jb2wgPSAxCikKYGBgCgpgYGB7ciwgZmlnLndpZHRoID0gMTQsIGZpZy5oZWlnaHQgPSA2fQpjb2xfMSA8LSBwbG90X2dyaWQoCiAgcGx0X3NydCwKICBwbHRfbWV0YSwKICBuY29sID0gMSwKICBhbGlnbiA9ICJ2IiwgYXhpcyA9ICJsciIsCiAgbGFiZWxzID0gTEVUVEVSU1tjKDEsMyldLAogIGxhYmVsX3ggPSAtMC4wNQopCgpjb2xfMiA8LSBwbG90X2dyaWQoCiAgcGx0X21heF9hbGxlbGUsCiAgcGx0X21ldGFfY29ycl9hYmJyZXYsCiAgbmNvbCA9IDEsCiAgcmVsX2hlaWdodHMgPSBjKDMsNSksCiAgYWxpZ24gPSAidiIsIGF4aXMgPSAibHIiLAogIGxhYmVscyA9IExFVFRFUlNbYygyLDQpXSwKICBsYWJlbF94ID0gLTAuMDUKKQoKcGx0X2ZpZ19tYWluIDwtIHBsb3RfZ3JpZCgKICBOVUxMLAogIGNvbF8xLAogIGNvbF8yLAogIG5yb3cgPSAxLAogIHJlbF93aWR0aHMgPSBjKDAuMjUsMyw2KQopCgpwbHRfZmlnX21haW4KYGBgCmBgYHtyfQpzYXZlX3Bsb3QoImZpZ3VyZXNfcGRmL3YyLzdfc2NITEEucGRmIiwgcGx0X2ZpZ19tYWluLCBiYXNlX2hlaWdodCA9IDcsIGJhc2Vfd2lkdGggPSAxNCkKYGBgCgojIFNhdmUgYWxsIGZpZ3VyZSBvYmplY3RzIApgYGB7cn0KbGlzdE4gPC0gZnVuY3Rpb24oLi4uKSB7CiAgYW5vbkxpc3QgPC0gbGlzdCguLi4pCiAgbmFtZXMoYW5vbkxpc3QpIDwtIGFzLmNoYXJhY3RlcihzdWJzdGl0dXRlKGxpc3QoLi4uKSkpWy0xXQogIGFub25MaXN0Cn0KCnNhdmVSRFMoCiAgbGlzdE4ocGx0X3NydCwKICAgICAgICBwbHRfbWV0YSwKICAgICAgICBwbHRfbWF4X2FsbGVsZSwKICAgICAgICBwbHRfbWV0YV9jb3JyX2FiYnJldgogICAgICAgICksCiAgImZpZ3VyZXNfb2JqZWN0Lzdfc2NITEFfb2JqZWN0cy5SRFMiCikKYGBgCgojIE9sZCB3b3JrCgo8IS0tIGBgYHtyfSAtLT4KPCEtLSBzY19tZXRhIDwtIGZ1bmN0aW9uKGRmKXsgLS0+CjwhLS0gICBtIDwtIG1ldGFiaW4oZXZlbnQuZSA9IG9ic2VydmVkLCBuLmUgPSBnZW5lX3N1bV90eXBlZCwgZXZlbnQuYyA9IGV4cGVjdGVkLCBuLmMgPSBnZW5lX3N1bV90eXBlZCwgLS0+CjwhLS0gICAgICAgICBkYXRhID0gZGYsIC0tPgo8IS0tICAgICAgICAgbWV0aG9kID0gIkludmVyc2UiLCAtLT4KPCEtLSAgICAgICAgIGluY3IgPSAwLjEsIC0tPgo8IS0tICAgICAgICAgc20gPSAiT1IiKSAtLT4KPCEtLSAgIGRhdGEuZnJhbWUoc3VtbWFyeShtKSRyYW5kb20pICU+JSAgLS0+CjwhLS0gICAgIHNlbGVjdChURSwgc2VURSwgbG93ZXIsIHVwcGVyKSAlPiUgIC0tPgo8IS0tICAgICBtdXRhdGVfYWxsKGV4cCkgLS0+CjwhLS0gfSAtLT4KPCEtLSBgYGAgLS0+CgoKPCEtLSBgYGB7cn0gLS0+CjwhLS0gc2FtcCA8LSAiSU5DT1YxWzAtMl1bMC05XS1CTCIgLS0+CjwhLS0geCA8LSB0aWJibGUoZ2Vub3R5cGVyID0gYygiaW52aXRybyIsICJhcmNhc0hMQSIsICJvcHRpdHlwZSIsICJwaGxhdCIsICJobGFtaW5lciIpKSAlPiUgIC0tPgo8IS0tICAgbXV0YXRlKGRhdGEgPSBtYXAoZ2Vub3R5cGVyLCBmdW5jdGlvbih4KSBzdXBwcmVzc01lc3NhZ2VzKHJlYWRfdHN2KCIuLi8uLi9jb3ZpZC9pc2Ivc2NITEFjb3VudC9CTF9mYXN0cV9maWxlcy50eHQiLCBjb2xfbmFtZXMgPSAic2FtcGxlIikpKSkgJT4lICAtLT4KPCEtLSAgIHVubmVzdChkYXRhKSAlPiUgIC0tPgo8IS0tICAgZmlsdGVyKGdyZXBsKHNhbXAsIHNhbXBsZSkpICU+JSAgLS0+CjwhLS0gICAjIEltcG9ydCBkYXRhIGJhc2VkIG9uIHNhbXBsZSBhbmQgZ2Vub3R5cGVyIC0tPgo8IS0tICAgbXV0YXRlKHJlc3VsdF9wYXRoID0gc3ByaW50ZigiJXMvb3V0cHV0LyVzIixzY0hMQWNvdW50X2RpciwgZ2Vub3R5cGVyKSwgLS0+CjwhLS0gICAgICAgICAgYmFyY29kZV9wYXRoID0gc3ByaW50ZigiJXMvYmFyY29kZXMiLCBzY0hMQWNvdW50X2RpcikpICU+JSAgLS0+CjwhLS0gICAjIGhlYWQoMikgJT4lIC0tPgo8IS0tICAgbXV0YXRlKGRhdGEgPSBwbWFwKGxpc3Qoc2FtcGxlLCByZXN1bHRfcGF0aCwgYmFyY29kZV9wYXRoKSwgZnVuY3Rpb24ocyxyLGIpeyAtLT4KPCEtLSAgICAgc2NITEFfZGF0YV9wcm9jZXNzaW5nKCAtLT4KPCEtLSAgICAgICBzYW1wbGU9cywgLS0+CjwhLS0gICAgICAgcmVzdWx0X2Rpcj1yLCAtLT4KPCEtLSAgICAgICBiYXJjb2RlX2Rpcj1iIC0tPgo8IS0tICAgICApIC0tPgo8IS0tICAgfSkpICU+JSB1bm5lc3QoZGF0YSkgLS0+CjwhLS0geCAtLT4KPCEtLSB5IDwtIHggJT4lICAtLT4KPCEtLSAgIGZpbHRlcihjZWxsICVpbiUgY2VsbF9saXN0KSAlPiUgIC0tPgo8IS0tICAgbXV0YXRlKHNhbXBsZSA9IGdzdWIoIl9bQS1aXVswLTldJCIsIiIsc2FtcGxlKSkgJT4lICAtLT4KPCEtLSAgICMgIyBmaWx0ZXIobl9hbGxlbGVzX29ic2VydmVkID09IDIsIGdyZXBsKCJeRFtQUVJdIiwgZ2VuZSksIHNhbXBsZUlEICVpbiUgYygiSU5DT1YwMDMtQUMiLCAiSU5DT1YwMjQtQUMiKSkgJT4lICAtLT4KPCEtLSAgICMgc2VsZWN0KGNlbGwsIGxvY3VzLCBjb3VudCwgZ2VuZV9zdW1fdHlwZWQsIGNlbGx0eXBlKSAlPiUgIC0tPgo8IS0tICAgIyBncm91cF9ieShzZXZlcml0eSwgY2VsbHR5cGUsIGdlbmUpICU+JSAgLS0+CjwhLS0gICBmaWx0ZXIoZ2VuZSA9PSAiQSIpICU+JSAgLS0+CjwhLS0gICBncm91cF9ieShzYW1wbGUsIGdlbm90eXBlcikgJT4lICAtLT4KPCEtLSAgIHNhbXBsZV9uKDUwMCwgcmVwbGFjZSA9IFQpICU+JSAtLT4KPCEtLSAgIG11dGF0ZShjb21wbGVtZW50ID0gZ2VuZV9zdW1fdHlwZWQgLSBjb3VudCwgZXhwZWN0ZWQgPSAwLjUqZ2VuZV9zdW1fdHlwZWQpICU+JSAgLS0+CjwhLS0gICByb3d3aXNlKCkgJT4lICAtLT4KPCEtLSAgIG11dGF0ZShvYnNlcnZlZCA9IG1heChhY3Jvc3MoYyhjb3VudCwgY29tcGxlbWVudCkpKSkgJT4lICAtLT4KPCEtLSAgIGdyb3VwX2J5KHNhbXBsZSwgZ2Vub3R5cGVyKSAlPiUgIC0tPgo8IS0tICAgbmVzdCgpICU+JSAgLS0+CjwhLS0gICBtdXRhdGUoZGF0YSA9IG1hcChkYXRhLCBzY19tZXRhKSkgJT4lICAtLT4KPCEtLSAgIHVubmVzdChkYXRhKSAtLT4KPCEtLSBgYGAgLS0+Cgo8IS0tIGBgYHtyLCBmaWcud2lkdGggPSAyNCwgZmlnLmhlaWdodCA9IDR9IC0tPgo8IS0tIHkgJT4lIC0tPgo8IS0tICAgbXV0YXRlKGdlbm90eXBlciA9IGZhY3RvcihnZW5vdHlwZXIsIGxldmVscyA9IGMoImhsYW1pbmVyIiwicGhsYXQiLCJvcHRpdHlwZSIsImFyY2FzSExBIiwiaW52aXRybyIpKSkgJT4lICAtLT4KPCEtLSAgIGdncGxvdChhZXMoeCA9IGdlbm90eXBlciwgeSA9IFRFLCB5bWluID0gbG93ZXIsIHltYXggPSB1cHBlcikpKyAtLT4KPCEtLSAgIGdlb21fcG9pbnQoKSsgLS0+CjwhLS0gICBnZW9tX2Vycm9yYmFyKCkrIC0tPgo8IS0tICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMSwgbGluZXR5cGUgPSAiZGFzaGVkIikrIC0tPgo8IS0tICAgZmFjZXRfZ3JpZCgufnNhbXBsZSwgc2NhbGVzID0gImZyZWUiKSsgLS0+CjwhLS0gICB0aGVtZV9idygpKyAtLT4KPCEtLSAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApKSArIC0tPgo8IS0tICAgY29vcmRfZmxpcCgpICsgLS0+CjwhLS0gICBsYWJzKHkgPSAiT2RkcyByYXRpbyBvZiBkb21pbmFudCBhbGxlbGUiLCB4PSBOVUxMKSAtLT4KPCEtLSBgYGAgLS0+CgoKPCEtLSBgYGB7cn0gLS0+CjwhLS0gc2FtcCA8LSAiSU5DT1YwNjktQkwiIC0tPgo8IS0tIGNlbGxfbGlzdCA8LSBzcnQgJT4lIGZpbHRlcihjZWxsdHlwZSA9PSAiY0RDIikgJT4lIHB1bGwoY2VsbCkgLS0+CjwhLS0geCA8LSB0aWJibGUoZ2Vub3R5cGVyID0gYygiaW52aXRybyIsICJhcmNhc0hMQSIsICJvcHRpdHlwZSIsICJwaGxhdCIsICJobGFtaW5lciIpKSAlPiUgIC0tPgo8IS0tICAgbXV0YXRlKGRhdGEgPSBtYXAoZ2Vub3R5cGVyLCBmdW5jdGlvbih4KSBzdXBwcmVzc01lc3NhZ2VzKHJlYWRfdHN2KCIuLi8uLi9jb3ZpZC9pc2Ivc2NITEFjb3VudC9CTF9mYXN0cV9maWxlcy50eHQiLCBjb2xfbmFtZXMgPSAic2FtcGxlIikpKSkgJT4lICAtLT4KPCEtLSAgIHVubmVzdChkYXRhKSAlPiUgIC0tPgo8IS0tICAgZmlsdGVyKGdyZXBsKHNhbXAsIHNhbXBsZSkpICU+JSAgLS0+CjwhLS0gICAjIEltcG9ydCBkYXRhIGJhc2VkIG9uIHNhbXBsZSBhbmQgZ2Vub3R5cGVyIC0tPgo8IS0tICAgbXV0YXRlKHJlc3VsdF9wYXRoID0gc3ByaW50ZigiJXMvb3V0cHV0LyVzIixzY0hMQWNvdW50X2RpciwgZ2Vub3R5cGVyKSwgLS0+CjwhLS0gICAgICAgICAgYmFyY29kZV9wYXRoID0gc3ByaW50ZigiJXMvYmFyY29kZXMiLCBzY0hMQWNvdW50X2RpcikpICU+JSAgLS0+CjwhLS0gICAjIGhlYWQoMikgJT4lIC0tPgo8IS0tICAgbXV0YXRlKGRhdGEgPSBwbWFwKGxpc3Qoc2FtcGxlLCByZXN1bHRfcGF0aCwgYmFyY29kZV9wYXRoKSwgZnVuY3Rpb24ocyxyLGIpeyAtLT4KPCEtLSAgICAgc2NITEFfZGF0YV9wcm9jZXNzaW5nKCAtLT4KPCEtLSAgICAgICBzYW1wbGU9cywgLS0+CjwhLS0gICAgICAgcmVzdWx0X2Rpcj1yLCAtLT4KPCEtLSAgICAgICBiYXJjb2RlX2Rpcj1iIC0tPgo8IS0tICAgICApIC0tPgo8IS0tICAgfSkpICU+JSB1bm5lc3QoZGF0YSkgLS0+CjwhLS0geSA8LSB4ICU+JSAgLS0+CjwhLS0gICBmaWx0ZXIoY2VsbCAlaW4lIGNlbGxfbGlzdCkgJT4lICAtLT4KPCEtLSAgIG11dGF0ZShzYW1wbGUgPSBnc3ViKCJfW0EtWl1bMC05XSQiLCIiLHNhbXBsZSkpICU+JSAgLS0+CjwhLS0gICBmaWx0ZXIoZ2VuZSA9PSAiQSIpICU+JSAgLS0+CjwhLS0gICBncm91cF9ieShzYW1wbGUsIGdlbm90eXBlcikgJT4lICAtLT4KPCEtLSAgIHNhbXBsZV9uKDUwMCwgcmVwbGFjZSA9IFQpICU+JSAtLT4KPCEtLSAgIG11dGF0ZShjb21wbGVtZW50ID0gZ2VuZV9zdW1fdHlwZWQgLSBjb3VudCwgZXhwZWN0ZWQgPSAwLjUqZ2VuZV9zdW1fdHlwZWQpICU+JSAgLS0+CjwhLS0gICByb3d3aXNlKCkgJT4lICAtLT4KPCEtLSAgIG11dGF0ZShvYnNlcnZlZCA9IG1heChhY3Jvc3MoYyhjb3VudCwgY29tcGxlbWVudCkpKSkgJT4lICAtLT4KPCEtLSAgIGdyb3VwX2J5KHNhbXBsZSwgZ2Vub3R5cGVyKSAlPiUgIC0tPgo8IS0tICAgbmVzdCgpICU+JSAgLS0+CjwhLS0gICBtdXRhdGUoZGF0YSA9IG1hcChkYXRhLCBzY19tZXRhKSkgJT4lICAtLT4KPCEtLSAgIHVubmVzdChkYXRhKSAtLT4KPCEtLSBwbHRfbWV0YSA8LSB5ICU+JSAgLS0+CjwhLS0gICB1bmdyb3VwKCkgJT4lICAtLT4KPCEtLSAgIG11dGF0ZShnZW5vdHlwZXIgPSByZWZvcm1hdF9obGFfZ2Vub3R5cGVyKGdlbm90eXBlcikpICU+JSAgLS0+CjwhLS0gICBnZ3Bsb3QoYWVzKHggPSBnZW5vdHlwZXIsIHkgPSBURSwgeW1pbiA9IGxvd2VyLCB5bWF4ID0gdXBwZXIpKSsgLS0+CjwhLS0gICBnZW9tX3BvaW50KCkrIC0tPgo8IS0tICAgZ2VvbV9lcnJvcmJhcigpKyAtLT4KPCEtLSAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDEsIGxpbmV0eXBlID0gImRhc2hlZCIpKyAtLT4KPCEtLSAgIHRoZW1lX2J3KCkrIC0tPgo8IS0tICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkgKyAtLT4KPCEtLSAgIGxhYnMoeSA9ICJPZGRzIHJhdGlvIG9mIGRvbWluYW50IGFsbGVsZSIsIHg9IE5VTEwpIC0tPgo8IS0tIHBsdF9tZXRhICAgLS0+CjwhLS0gYGBgIC0tPgo8IS0tICMjIyBBbGwgY2x1c3RlcnMgLS0+Cgo8IS0tIGBgYHtyfSAtLT4KPCEtLSBzYW1wIDwtICJJTkNPVjA2OS1CTCIgLS0+CjwhLS0gY2VsbF9saXN0IDwtIHNydCAlPiUgZmlsdGVyKGNlbGx0eXBlID09ICJjREMiKSAlPiUgcHVsbChjZWxsKSAtLT4KPCEtLSB4IDwtIHRpYmJsZShnZW5vdHlwZXIgPSBjKCJpbnZpdHJvIiwgImFyY2FzSExBIiwgIm9wdGl0eXBlIiwgInBobGF0IiwgImhsYW1pbmVyIikpICU+JSAgLS0+CjwhLS0gICBtdXRhdGUoZGF0YSA9IG1hcChnZW5vdHlwZXIsIGZ1bmN0aW9uKHgpIHN1cHByZXNzTWVzc2FnZXMocmVhZF90c3YoIi4uLy4uL2NvdmlkL2lzYi9zY0hMQWNvdW50L0JMX2Zhc3RxX2ZpbGVzLnR4dCIsIGNvbF9uYW1lcyA9ICJzYW1wbGUiKSkpKSAlPiUgIC0tPgo8IS0tICAgdW5uZXN0KGRhdGEpICU+JSAgLS0+CjwhLS0gICBmaWx0ZXIoZ3JlcGwoc2FtcCwgc2FtcGxlKSkgJT4lICAtLT4KPCEtLSAgICMgSW1wb3J0IGRhdGEgYmFzZWQgb24gc2FtcGxlIGFuZCBnZW5vdHlwZXIgLS0+CjwhLS0gICBtdXRhdGUocmVzdWx0X3BhdGggPSBzcHJpbnRmKCIlcy9vdXRwdXQvJXMiLHNjSExBY291bnRfZGlyLCBnZW5vdHlwZXIpLCAtLT4KPCEtLSAgICAgICAgICBiYXJjb2RlX3BhdGggPSBzcHJpbnRmKCIlcy9iYXJjb2RlcyIsIHNjSExBY291bnRfZGlyKSkgJT4lICAtLT4KPCEtLSAgICMgaGVhZCgyKSAlPiUgLS0+CjwhLS0gICBtdXRhdGUoZGF0YSA9IHBtYXAobGlzdChzYW1wbGUsIHJlc3VsdF9wYXRoLCBiYXJjb2RlX3BhdGgpLCBmdW5jdGlvbihzLHIsYil7IC0tPgo8IS0tICAgICBzY0hMQV9kYXRhX3Byb2Nlc3NpbmcoIC0tPgo8IS0tICAgICAgIHNhbXBsZT1zLCAtLT4KPCEtLSAgICAgICByZXN1bHRfZGlyPXIsIC0tPgo8IS0tICAgICAgIGJhcmNvZGVfZGlyPWIgLS0+CjwhLS0gICAgICkgLS0+CjwhLS0gICB9KSkgJT4lIHVubmVzdChkYXRhKSAtLT4KPCEtLSByZXNhbXBsZV9kZXB0aCA8LSA1MDAgLS0+CjwhLS0geSA8LSB4ICU+JSAgLS0+CjwhLS0gICBsZWZ0X2pvaW4oc3J0ICU+JSBzZWxlY3QoY2VsbHR5cGUsIGNlbGwpLCBieSA9ICJjZWxsIikgJT4lICAtLT4KPCEtLSAgIG11dGF0ZShzYW1wbGUgPSBnc3ViKCJfW0EtWl1bMC05XSQiLCIiLHNhbXBsZSkpICU+JSAgLS0+CjwhLS0gICAjIGZpbHRlcihnZW5lID09ICJBIikgJT4lICAtLT4KPCEtLSAgIGZpbHRlcighaXMubmEoY2VsbHR5cGUpKSAlPiUgIC0tPgo8IS0tICAgdW5pdGUoImdyb3VwIiwgY2VsbHR5cGUsIGdlbmUsIHNlcCA9ICJfIiwgcmVtb3ZlID0gRikgJT4lICAtLT4KPCEtLSAgICMgbXV0YXRlKGdyb3VwID0gY2VsbHR5cGUpICU+JSAgLS0+CjwhLS0gICBncm91cF9ieShncm91cCkgJT4lIG5lc3QoKSAlPiUgIC0tPgo8IS0tICAgbXV0YXRlKGRhdGEgPSBtYXAoZGF0YSwgZnVuY3Rpb24oeCl7IC0tPgo8IS0tICAgICB4ICU+JSAgLS0+CjwhLS0gICAgICAgZ3JvdXBfYnkoc2FtcGxlLCBnZW5vdHlwZXIpICU+JSAgLS0+CjwhLS0gICAgICAgIyBzYW1wbGVfbihyZXNhbXBsZV9kZXB0aCwgcmVwbGFjZSA9IFQpICU+JSAgLS0+CjwhLS0gICAgICAgbXV0YXRlKGNvbXBsZW1lbnQgPSBnZW5lX3N1bV90eXBlZCAtIGNvdW50LCBleHBlY3RlZCA9IDAuNSpnZW5lX3N1bV90eXBlZCkgJT4lICAtLT4KPCEtLSAgICAgICByb3d3aXNlKCkgJT4lICAtLT4KPCEtLSAgICAgICBtdXRhdGUob2JzZXJ2ZWQgPSBtYXgoYWNyb3NzKGMoY291bnQsIGNvbXBsZW1lbnQpKSkpICU+JSAgLS0+CjwhLS0gICAgICAgZ3JvdXBfYnkoc2FtcGxlLCBnZW5vdHlwZXIpICU+JSAgLS0+CjwhLS0gICAgICAgbmVzdCgpICU+JSAgLS0+CjwhLS0gICAgICAgbXV0YXRlKGRhdGEgPSBtYXAoZGF0YSwgc2NfbWV0YSkpICU+JSAgLS0+CjwhLS0gICAgICAgdW5uZXN0KGRhdGEpIC0tPgo8IS0tICAgfSkpICU+JSAgLS0+CjwhLS0gICB1bm5lc3QoZGF0YSkgLS0+CjwhLS0gYGBgIC0tPgoKPCEtLSBgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0ID0gNH0gLS0+CjwhLS0geSA8LSB5ICU+JSAgLS0+CjwhLS0gICBzZXBhcmF0ZShncm91cCwgaW50byA9IGMoImNlbGx0eXBlIiwgImdlbmUiKSwgc2VwID0gIl8iKSAlPiUgIC0tPgo8IS0tICAgbXV0YXRlKGdyb3VwID0gZmFjdG9yKGNlbGx0eXBlLCBsZXZlbHMgPSBjKCAtLT4KPCEtLSAgICAgImNEQyIsICJDRDE0IE1vbm9jeXRlIiwgIkIiLCAicERDIiwgIkNEMTYgTW9ub2N5dGUiLCAiQ0Q4IFQiLCAiQ0Q0IFQiLCAiTksiIC0tPgo8IS0tICAgKSkpIC0tPgo8IS0tIHkgJT4lICAtLT4KPCEtLSAgIHVuZ3JvdXAoKSAlPiUgIC0tPgo8IS0tICAgbXV0YXRlKGdlbm90eXBlciA9IHJlZm9ybWF0X2hsYV9nZW5vdHlwZXIoZ2Vub3R5cGVyKSkgJT4lICAtLT4KPCEtLSAgIGdncGxvdChhZXMoeCA9IGdlbm90eXBlciwgeSA9IFRFLCB5bWluID0gbG93ZXIsIHltYXggPSB1cHBlciwgZmlsbCA9IGdyb3VwKSkrIC0tPgo8IS0tICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIiwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMC4yNSkrIC0tPgo8IS0tICAgZ2VvbV9lcnJvcmJhcihwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuOSksIHdpZHRoID0wLjUpKyAtLT4KPCEtLSAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDEsIGxpbmV0eXBlID0gImRhc2hlZCIpKyAtLT4KPCEtLSAgIHRoZW1lX2J3KCkrIC0tPgo8IS0tICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkgKyAtLT4KPCEtLSAgIGxhYnMoeSA9ICJPZGRzIHJhdGlvIG9mIGRvbWluYW50IGFsbGVsZSIsIHg9IE5VTEwpICsgLS0+CjwhLS0gICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIkRhcmsyIikgLS0+Cgo8IS0tIHkgJT4lICAtLT4KPCEtLSAgIHVuZ3JvdXAoKSAlPiUgIC0tPgo8IS0tICAgbXV0YXRlKGdlbm90eXBlciA9IHJlZm9ybWF0X2hsYV9nZW5vdHlwZXIoZ2Vub3R5cGVyKSkgJT4lICAtLT4KPCEtLSAgIGdncGxvdChhZXMoeCA9IGdlbm90eXBlciwgeSA9IFRFLCB5bWluID0gbG93ZXIsIHltYXggPSB1cHBlciwgZmlsbCA9IGdyb3VwKSkrIC0tPgo8IS0tICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIiwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMC4yNSkrIC0tPgo8IS0tICAgZ2VvbV9lcnJvcmJhcihwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuOSksIHdpZHRoID0wLjUpKyAtLT4KPCEtLSAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDEsIGxpbmV0eXBlID0gImRhc2hlZCIpKyAtLT4KPCEtLSAgIHRoZW1lX2J3KCkrIC0tPgo8IS0tICAgZmFjZXRfd3JhcCh+Z3JvdXAsIG5jb2wgPSA0KSsgLS0+CjwhLS0gICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSArIC0tPgo8IS0tICAgbGFicyh5ID0gIk9kZHMgcmF0aW8gb2YgZG9taW5hbnQgYWxsZWxlIiwgeD0gTlVMTCkgKyAtLT4KPCEtLSAgIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiRGFyazIiKSAtLT4KCjwhLS0geSAlPiUgIC0tPgo8IS0tICAgdW5ncm91cCgpICU+JSAgLS0+CjwhLS0gICBtdXRhdGUoZ2Vub3R5cGVyID0gcmVmb3JtYXRfaGxhX2dlbm90eXBlcihnZW5vdHlwZXIpKSAlPiUgIC0tPgo8IS0tICAgZ2dwbG90KGFlcyh4ID0gZ3JvdXAsIHkgPSBURSwgeW1pbiA9IGxvd2VyLCB5bWF4ID0gdXBwZXIsIGZpbGwgPSBjZWxsdHlwZSkpKyAtLT4KPCEtLSAgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIsIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDAuMjUpKyAtLT4KPCEtLSAgIGdlb21fZXJyb3JiYXIocG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgwLjkpLCB3aWR0aCA9MC41KSsgLS0+CjwhLS0gICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAxLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSsgLS0+CjwhLS0gICB0aGVtZV9idygpKyAtLT4KPCEtLSAgIGZhY2V0X2dyaWQoZ2VuZX5nZW5vdHlwZXIsIHNjYWxlcyA9ICJmcmVlX3kiKSsgLS0+CjwhLS0gICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSArIC0tPgo8IS0tICAgbGFicyh5ID0gIk9kZHMgcmF0aW8gb2YgZG9taW5hbnQgYWxsZWxlIiwgeD0gTlVMTCkgKyAtLT4KPCEtLSAgIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiRGFyazIiKSAtLT4KCjwhLS0geSAlPiUgIC0tPgo8IS0tICAgdW5ncm91cCgpICU+JSAgLS0+CjwhLS0gICBmaWx0ZXIoZ3JvdXAgPT0gInBEQyIpICU+JSAgLS0+CjwhLS0gICBtdXRhdGUoZ2Vub3R5cGVyID0gcmVmb3JtYXRfaGxhX2dlbm90eXBlcihnZW5vdHlwZXIpKSAlPiUgIC0tPgo8IS0tICAgZ2dwbG90KGFlcyh4ID0gZ2Vub3R5cGVyLCB5ID0gVEUsIHltaW4gPSBsb3dlciwgeW1heCA9IHVwcGVyKSkrIC0tPgo8IS0tICAgZ2VvbV9wb2ludCgpKyAtLT4KPCEtLSAgIGdlb21fZXJyb3JiYXIoKSsgLS0+CjwhLS0gICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAxLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSsgLS0+CjwhLS0gICB0aGVtZV9idygpKyAtLT4KPCEtLSAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpICsgLS0+CjwhLS0gICBsYWJzKHkgPSAiT2RkcyByYXRpbyBvZiBkb21pbmFudCBhbGxlbGUiLCB4PSBOVUxMKSAtLT4KPCEtLSBgYGAgLS0+CgoKCgo8IS0tICMgQXNzZW1ibGUgcGxvdCAtLT4KPCEtLSBgYGB7ciwgZmlnLndpZHRoID0gMTQsIGZpZy5oZWlnaHQgPSA2fSAtLT4KPCEtLSAjIHBsdF9hbGxlbGVfZnJlcV9sZ2QgPC0gY293cGxvdDo6Z2V0X2xlZ2VuZChwbHRfYWxsZWxlX2ZyZXEpIC0tPgo8IS0tICMgcGx0X21heF9hbGxlbGVfbGdkIDwtIGNvd3Bsb3Q6OmdldF9sZWdlbmQocGx0X21heF9hbGxlbGUpIC0tPgo8IS0tIGNvbF8xIDwtIHBsb3RfZ3JpZCggLS0+CjwhLS0gICBwbHRfYWxsZWxlX2ZyZXEsIC0tPgo8IS0tICAgcGx0X21heF9hbGxlbGUsIC0tPgo8IS0tICAgbmNvbCA9IDEsIC0tPgo8IS0tICAgcmVsX2hlaWdodHMgPSBjKDUsMyksIC0tPgo8IS0tICAgYWxpZ24gPSAidiIsIGF4aXMgPSAibHIiLCAtLT4KPCEtLSAgIGxhYmVscyA9IExFVFRFUlNbMToyXSAtLT4KPCEtLSApIC0tPgoKPCEtLSBjb2xfMiA8LSBwbG90X2dyaWQoIC0tPgo8IS0tICAgcGx0X3NydCwgLS0+CjwhLS0gICBwbHRfbWV0YSwgLS0+CjwhLS0gICBuY29sID0gMSwgLS0+CjwhLS0gICBhbGlnbiA9ICJ2IiwgYXhpcyA9ICJsciIsIC0tPgo8IS0tICAgbGFiZWxzID0gTEVUVEVSU1szOjRdLCAtLT4KPCEtLSAgIGhqdXN0ID0gMC41IC0tPgo8IS0tICkgLS0+CjwhLS0gcGxvdF9ncmlkKCAtLT4KPCEtLSAgIGNvbF8xLCAtLT4KPCEtLSAgIGNvbF8yLCAgLS0+CjwhLS0gICBucm93ID0gMSwgLS0+CjwhLS0gICByZWxfd2lkdGhzID0gYyg2LDMpIC0tPgo8IS0tICkgLS0+CjwhLS0gYGBgIC0tPgoKCgoKCgoKPCEtLSBgYGB7cn0gLS0+CjwhLS0gZyA8LSBnZ3Bsb3RfYnVpbGQocGx0X3NydCkgLS0+Cgo8IS0tIHBsdF9pZHMgPC0gZyRkYXRhW1sxXV0gLS0+CjwhLS0gZ3JvdXBfbGV2ZWxzIDwtIGxldmVscyhmYWN0b3IoZyRwbG90JGRhdGFbW2ckcGxvdCRsYWJlbHMkY29sb3VyXV0pKSAtLT4KCjwhLS0gcGx0X2tleSA8LSBnJGRhdGFbWzFdXSAlPiUgIC0tPgo8IS0tICAgc2VsZWN0KGNvbG91ciwgZ3JvdXApICU+JSAgLS0+CjwhLS0gICBkaXN0aW5jdCgpICU+JSAgLS0+CjwhLS0gICBtdXRhdGUobGFiZWwgPSBtYXBfY2hyKGdyb3VwLCBmdW5jdGlvbih4KSBncm91cF9sZXZlbHNbeF0pKSAlPiUgIC0tPgo8IS0tICAgbXV0YXRlKGxhYmVsID0gZmFjdG9yKGxhYmVsLCBsZXZlbCA9IGdyb3VwX2xldmVscykpICU+JSAtLT4KPCEtLSAgIG11dGF0ZShsYWJlbCA9IHNwcmludGYoIiVzKSAlcyIsIDE6bigpLCBsYWJlbCkpICU+JSAgLS0+CjwhLS0gICBzZWxlY3QoLWdyb3VwKSAtLT4KCjwhLS0gICAjIGxldmVscyhmYWN0b3IoZyRwbG90JGRhdGFbW2ckcGxvdCRsYWJlbHMkY29sb3VyXV0pKSAtLT4KPCEtLSAgICMgZGF0YS5mcmFtZShjb2xvdXJzID0gdW5pcXVlKGckZGF0YVtbMV1dWyJjb2xvdXIiXSksICAtLT4KPCEtLSAgICMgICAgICAgICAgICAgIGxhYmVsID0gbGV2ZWxzKGZhY3RvcihnJHBsb3QkZGF0YVtbZyRwbG90JGxhYmVscyRjb2xvdXJdXSkpKSAlPiUgIC0tPgoKPCEtLSBwbHRfZGYgPC0gcGx0X2lkcyAlPiUgIC0tPgo8IS0tICAgbGVmdF9qb2luKHBsdF9rZXksIGJ5ID0gImNvbG91ciIpIC0tPgoKPCEtLSBwbHRfY2VudGVyIDwtIHBsdF9kZiAlPiUgIC0tPgo8IS0tICAgZ3JvdXBfYnkobGFiZWwpICU+JSBzdW1tYXJpc2UoeCA9IG1lYW4oeCksIHkgPSBtZWFuKHkpKSAlPiUgLS0+CjwhLS0gICBtdXRhdGUobGFiZWwgPSBnc3ViKCIpLioiLCIiLGxhYmVsKSkgLS0+Cgo8IS0tIHBsdF9yZXBlbCA8LSBwbHRfZGYgJT4lICAtLT4KPCEtLSAgIGdncGxvdChhZXMoeD14LHk9eSxjb2xvcj1sYWJlbCkpICsgLS0+CjwhLS0gICBnZW9tX3BvaW50KHNpemUgPSAwLjUpKyAtLT4KPCEtLSAgICMgZ2VvbV9wb2ludChkYXRhID0gcGx0X2NlbnRlciwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMikgLS0+CjwhLS0gICBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwoZGF0YT1wbHRfY2VudGVyLGFlcyhsYWJlbD1sYWJlbCwgIGJnLmNvbG9yPSJ3aGl0ZSIsIGJnLnI9MC4yNSxtaW4uc2VnbWVudC5sZW5ndGggPSAwKSxjb2xvciA9ICJibGFjayIsZm9udGZhY2UgPSAiYm9sZCIpICsgLS0+CjwhLS0gICB0aGVtZV9idygpICsgLS0+CjwhLS0gICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gMikgKSApICsgLS0+CjwhLS0gICBsYWJzKHg9IlVNQVAgMSIsIHkgPSAiVU1BUCAyIiwgY29sb3IgPSAiQ2VsbCBDbHVzdGVyIikgKyAtLT4KPCEtLSAgIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIkRhcmsyIikrICAtLT4KPCEtLSAgIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSkgLS0+CjwhLS0gcGx0X3JlcGVsIC0tPgoKPCEtLSBgYGAgLS0+CgoKCgoKCg==